From c7486c0b423959fbf7501f65f651183c78bf31f6 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 17:09:33 +0100 Subject: [PATCH 01/27] Add sphinx_gated_directives extension and code-align CSS Introduces the custom Sphinx extension 'sphinx_gated_directives' for start/end directive gating and validation, registering it in the Sphinx local extensions. Also adds a CSS file to enforce left alignment for code cells. Updates _config.yml to configure the new extension and set the documentation language to English. --- book/_config.yml | 5 +- book/_ext/sphinx_gated_directives/__init__.py | 520 ++++++++++++++++++ book/_static/code-align.css | 4 + 3 files changed, 527 insertions(+), 2 deletions(-) create mode 100644 book/_ext/sphinx_gated_directives/__init__.py create mode 100644 book/_static/code-align.css diff --git a/book/_config.yml b/book/_config.yml index 2c6585c..580aa76 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -11,15 +11,16 @@ html: baseurl : "https://teachbooks.io/manual/" sphinx: + local_extensions: + sphinx_gated_directives: _ext extra_extensions: - sphinx_inline_tabs - sphinx.ext.imgconverter - teachbooks_favourites - teachbooks_sphinx_grasple - sphinx_apa_references - - sphinx_github_alerts - - sphinx_metadata_figure config: + language: en html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js thebe_config: diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py new file mode 100644 index 0000000..e3cb8b0 --- /dev/null +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -0,0 +1,520 @@ +# startify_directives_all.py +""" +Create a '-start' companion for every registered directive (class-based). + +Rules: + - Skip originals whose name already ends with '-start' or '-end'. + - Skip if '-start' already exists. + - Class-based originals -> register a subclass calling original run(). + +Usage: + extensions = ["startify_directives_all"] +""" + +from __future__ import annotations +from docutils.nodes import Element +from typing import Iterator +import re +import copy +import importlib +import inspect +from typing import Callable, Dict, Iterable, List, Optional +from sphinx.transforms import SphinxTransform +from sphinx.application import Sphinx +from sphinx.environment import BuildEnvironment + +from sphinx.util import logging +logger = logging.getLogger(__name__) + +from docutils import nodes +from docutils.parsers.rst import Directive +from docutils.parsers.rst import directives as du_directives + +SUFFIX_START = "-start" +SUFFIX_END = "-end" + + +# --------------------------- +# Helpers: identification +# --------------------------- + +def _is_class_directive(obj) -> bool: + return inspect.isclass(obj) and issubclass(obj, Directive) + + +# --------------------------- +# Helpers: registry access +# --------------------------- + +def _get_unified_registry(app=None) -> Dict[str, object]: + """ + Build a unified {name -> implementation} mapping of directives. + + Sources: + 1) docutils.parsers.rst.directives._directives (loaded/added so far) + 2) docutils.parsers.rst.directives._directive_registry (lazy map of + name -> (module, class) for builtins); import any missing ones. + 3) Sphinx app.registry (if provided) for extension-registered directives + """ + # 1) Already-loaded directives (includes Sphinx & extensions registered via add_directive) + unified: Dict[str, object] = dict(getattr(du_directives, "_directives", {})) + + # 2) Ensure Docutils built-ins are present by importing from _directive_registry + reg = getattr(du_directives, "_directive_registry", {}) + for name, (modname, clsname) in reg.items(): + if name in unified: + continue + try: + mod = importlib.import_module(f"docutils.parsers.rst.directives.{modname}") + obj = getattr(mod, clsname, None) + if obj is not None: + unified[name] = obj + except Exception: + # Be defensive: if an import fails, just skip that name + continue + + # 3) Add Sphinx's registry if app is provided + if app is not None: + # Sphinx stores directives in app.registry + # Try different ways to access the directives mapping + if hasattr(app.registry, 'directives'): + # Newer Sphinx versions may have a directives attribute + try: + unified.update(app.registry.directives) + except (AttributeError, TypeError): + pass + + # Also check for domains which contain directives (e.g., prf:theorem from sphinx-proof) + if hasattr(app.registry, 'domains'): + for domain_name, domain_cls in app.registry.domains.items(): + try: + # Instantiate domain or use class attributes + if hasattr(domain_cls, 'directives'): + domain_directives = domain_cls.directives + for dir_name, dir_impl in domain_directives.items(): + # Fully qualified name: domain:directive + full_name = f"{domain_name}:{dir_name}" + if full_name not in unified: + unified[full_name] = dir_impl + except Exception: + continue + + return unified + + +# --------------------------- +# Helpers: class generation +# --------------------------- + +def _copy_option_spec(option_spec): + return copy.copy(option_spec) if isinstance(option_spec, dict) else option_spec + +def make_startified_class( + orig_name: str, + base_cls: type[Directive], +) -> type[Directive]: + """ + Subclass `base_cls` to call original run(). + """ + attrs = {} + for attr in ( + "required_arguments", + "optional_arguments", + "final_argument_whitespace", + "has_content", + "option_spec", + ): + if hasattr(base_cls, attr): + val = getattr(base_cls, attr) + if attr == "option_spec": + attrs[attr] = _copy_option_spec(val) or {} + else: + attrs[attr] = val + + def run(self: Directive): + current_name = self.name + self.name = orig_name # temporarily set to original for base run() + children = base_cls.run(self) + self.name = current_name # restore + if not isinstance(children, list): + if isinstance(children, Iterable): + children = list(children) + else: + children = [children] + # create a start_node and add all result nodes as its children + start_node_instance = start_node() + start_node_instance += children + result = [start_node_instance] + + # Get environment from state.document.settings + env = getattr(self.state.document.settings, "env", None) + if env is not None: + # 1) Check whether main registry is already created, if not, create it + if not hasattr(env, "sphinx_gated_directives_registry"): + env.sphinx_gated_directives_registry = {} + # 2) Check whether a sub-registry for current directive type is already created, if not, create it + if orig_name not in env.sphinx_gated_directives_registry: + env.sphinx_gated_directives_registry[orig_name] = {} + # 3) Register current usage in the registry for this doc and this type + gated_registry = env.sphinx_gated_directives_registry[orig_name] + docname = env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + } + gated_registry[docname]["start"].append(self.lineno) + gated_registry[docname]["sequence"].append("S") + gated_registry[docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + # 4) Check whether super registry has been created, if not, create it + if not hasattr(env, "sphinx_gated_directives_super_registry"): + env.sphinx_gated_directives_super_registry = {} + # 5) Register current usage in the super registry + super_registry = env.sphinx_gated_directives_super_registry + if docname not in super_registry: + super_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": [], + } + super_registry[docname]["start"].append(self.lineno) + super_registry[docname]["sequence"].append("S") + super_registry[docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + super_registry[docname]["type"].append(orig_name) + + return result + + attrs["run"] = run + new_cls_name = f"{base_cls.__name__}_Start_For_{orig_name.replace(':', '_')}" + return type(new_cls_name, (base_cls,), attrs) + +def make_endified_class( + orig_name: str, + base_cls: type[Directive], +) -> type[Directive]: + """ + Just create a subclass to obtain an end node. + """ + attrs = {} + for attr in ( + "required_arguments", + "optional_arguments", + "final_argument_whitespace", + "has_content", + "option_spec", + ): + if hasattr(base_cls, attr): + val = getattr(base_cls, attr) + if attr == "option_spec": + attrs[attr] = _copy_option_spec(val) or {} + else: + attrs[attr] = val + + def run(self: Directive): + + # Get environment from state.document.settings + env = getattr(self.state.document.settings, "env", None) + if env is not None: + # 1) Check whether main registry is already created, if not, create it + if not hasattr(env, "sphinx_gated_directives_registry"): + env.sphinx_gated_directives_registry = {} + # 2) Check whether a sub-registry for current directive type is already created, if not, create it + if orig_name not in env.sphinx_gated_directives_registry: + env.sphinx_gated_directives_registry[orig_name] = {} + # 3) Register current usage in the registry for this doc and this type + gated_registry = env.sphinx_gated_directives_registry[orig_name] + docname = env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + } + gated_registry[docname]["end"].append(self.lineno) + gated_registry[docname]["sequence"].append("E") + gated_registry[docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + # 4) Check whether super registry has been created, if not, create it + if not hasattr(env, "sphinx_gated_directives_super_registry"): + env.sphinx_gated_directives_super_registry = {} + # 5) Register current usage in the super registry + super_registry = env.sphinx_gated_directives_super_registry + if docname not in super_registry: + super_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": [], + } + super_registry[docname]["end"].append(self.lineno) + super_registry[docname]["sequence"].append("E") + super_registry[docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + super_registry[docname]["type"].append(orig_name) + + # return [] + # next line should be good enough, but raises an error as not all has been implemented + # maybe we need end nodes per directive type? + return [end_node()] + + attrs["run"] = run + new_cls_name = f"{base_cls.__name__}_End_For_{orig_name.replace(':', '_')}" + return type(new_cls_name, (base_cls,), attrs) + +# --------------------------- +# Core logic +# --------------------------- + +def _should_skip_name(orig_name: str) -> bool: + return orig_name.endswith(SUFFIX_START) or orig_name.endswith(SUFFIX_END) + +def _register_startified_directives(app, env, docnames): + unified = _get_unified_registry(app) + snapshot_names = set(unified.keys()) + added = 0 + + for orig_name, obj in sorted(unified.items()): + if _should_skip_name(orig_name): + continue + + new_name = f"{orig_name}{SUFFIX_START}" + if new_name in snapshot_names: + continue + + try: + if _is_class_directive(obj): + start_cls = make_startified_class(orig_name, obj) + app.add_directive(new_name, start_cls) + end_cls = make_endified_class(orig_name, Directive) # because it does generate anything, most simple directive + app.add_directive(f"{orig_name}{SUFFIX_END}", end_cls) + added += 1 + else: + logger.debug(f"[startify] '{orig_name}' is not a class directive; skipping.") + except Exception as e: + logger.warning(f"[startify] failed to register '{new_name}': {e}") + +def setup(app): + + # register function to perge registries at start of build per document + app.connect("env-purge-doc", purge_registries) + + # At the latest possible moment, register startified directives (also creates the endified ones) + app.connect("env-before-read-docs", _register_startified_directives,priority=10000000000000000000000000000) + + # Register nodes, used as placeholder for end directives + # will be resolved in the transforms to something meaningful + # during merging of start-end blocks + app.add_node(start_node) + app.add_node(end_node) + + # Register a transform to check validity of start-end pairs + app.add_transform(CheckGatedDirectivesTransform) + + # Register a transform to merge start-end pairs into gated content + app.add_transform(MergeGatedDirectivesTransform) + + return { + "version": "1.0", + "parallel_read_safe": True, + } + +# Node classes for start and end directives + +class start_node(nodes.Admonition, nodes.Element): + pass + +class end_node(nodes.Admonition, nodes.Element): + pass + +# purge_registry +def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: + + if hasattr(env, "sphinx_gated_directives_registry") or hasattr(env, "sphinx_gated_directives_super_registry"): + # only perge if needed + if hasattr(env, "sphinx_gated_directives_registry"): + registry = env.sphinx_gated_directives_registry + for directive_name in registry: + if docname in registry[directive_name]: + del registry[directive_name][docname] + if hasattr(env, "sphinx_gated_directives_super_registry"): + super_registry = env.sphinx_gated_directives_super_registry + if docname in super_registry: + del super_registry[docname] + +# Transform to check validity of start-end pairs +class CheckGatedDirectivesTransform(SphinxTransform): + default_priority = 1 + + def apply(self, **kwargs): + env = self.env + if not hasattr(env, "sphinx_gated_directives_registry"): + return + if not hasattr(env, "sphinx_gated_directives_super_registry"): + return + + # First check the super registry for a correct structure + # demands: + # 1) equal number of start and end directives + # 2) no nesting of start-end pairs (so SSEE is not allowed, only SESE) + super_registry = env.sphinx_gated_directives_super_registry + error = False + docname = self.env.docname + if docname in super_registry: + start = super_registry[docname]["start"] + end = super_registry[docname]["end"] + structure = "\n ".join(super_registry[docname]["msg"]) + sequence = "".join(super_registry[docname]["sequence"]) + groups = re.findall("(SE)", sequence) + if len(start) > len(end): + msg = f"The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." + logger.error(msg) + error = True + elif len(end) > len(start): + msg = f"The document {docname} contains more end directives than start directives:\n {structure}\nPlease ensure each end directive has a corresponding start directive." + logger.error(msg) + error = True + # at this point, len(start) == len(end) + elif len(groups) != len(start) or len(groups) != len(end): + msg = f"The document {docname} contains nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." + logger.error(msg) + error = True + else: + # At this point, every start is followed by an end. + # Now check that types match in order. + types = super_registry[docname]["type"] + start_type = types[::2] + end_type = types[1::2] + for i in range(len(start_type)): + if start_type[i] != end_type[i]: + msg = f"The document {docname} contains mismatched start and end directives at lines {start[i]} and {end[i]}:\n {structure}\nPlease ensure that start and end directives match in type." + logger.error(msg) + error = True + + if error: + raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") + + # no check per directive type is needed if no error in super registry is raised, + # as the super registry already ensures correctness for each type. + # if nesting is allowed in the future, this part needs to be re-implemented. + + +class MergeGatedDirectivesTransform(SphinxTransform): + default_priority = 10 + + def apply(self, **kwargs): + env = self.env + # Something to do here + # idea: + # 1) Find all start_nodes + # 2) for each start_node, find the corresponding end_node (i.e. next end_node) + # 3) collect all nodes in between + # 4) Take the first node after start_node, and add all other collected nodes as its children + if not hasattr(env, "sphinx_gated_directives_super_registry"): + return + super_registry = env.sphinx_gated_directives_super_registry + docname = self.env.docname + if docname not in super_registry: + return + + # Each start-end pair is valid (checked before), so we can proceed to merge + # find all start nodes, as an iterator + # for each start node, find the next end node within the parent of start node + start_nodes = findall(self.document, start_node) + for start_n in start_nodes: + parent = start_n.parent + # find the next end_node after start_n in parent + found_start = False + end_n = None + for child in parent.children: + if child is start_n: + found_start = True + elif found_start and isinstance(child, end_node): + end_n = child + break + if end_n is None: + continue # should not happen due to prior checks + + # collect all nodes between start_n and end_n + start_index = parent.children.index(start_n) + end_index = parent.children.index(end_n) + between_nodes = parent.children[start_index + 1:end_index] + + # print the nodes for debugging + logger.info(f"{start_n.pformat()}",color="green") + if between_nodes: + for bn in between_nodes: + logger.info(f"{bn.pformat()}",color="yellow") + logger.info(f"{end_n.pformat()}",color="red") + + # We have to merge, but how is the question. + # we handle based on "experience" and "observation" of what kind of nodes are generated + # step 1: create a new container node to hold the merged content + # content of start_node + new_nodes = start_n.children + # add content in between, depending on content in start_n + if between_nodes: + has_section = False + has_caption = False + si = -1 + content = new_nodes[-1] # we assume that the last child of start_n holds the main content + for sn in content.children: + si += 1 + if isinstance(sn, nodes.section): + has_section = True # So probably a topic or similar (from sphinx-proof) + section_index = si + section_node = sn + break + elif isinstance(sn, nodes.caption): + has_caption = True # So probably a figure or similar + caption_index = si + caption_node = sn + break + if has_section: + # if there are sections, we add all between nodes to this section + content[section_index] += between_nodes + elif has_caption: + # if there is a caption, we add all between nodes before the caption + n = len(content.children) + pos = n - 1 + for i, bn in enumerate(between_nodes): + content.insert(pos + i, bn) # insert sets parent/doc links + else: + logger.info(f"Found no caption and no section node in start_node content.",color="blue") + # otherwise, we add all between nodes to the main content directly\ + for bn in between_nodes: + content += bn # add sets parent/doc links + logger.info(f"New nodes after merging:",color="blue") + for nn in new_nodes: + logger.info(f"{nn.pformat()}",color="blue") + + # finally, replace start_n with new_nodes, and remove end_n and between_nodes + start_pos = parent.children.index(start_n) + # remove end_n + parent.remove(end_n) + # remove start_n + parent.remove(start_n) + # remove between_nodes + for bn in between_nodes: + parent.remove(bn) + # insert new_nodes at start_pos + for i, nn in enumerate(new_nodes): + parent.insert(start_pos + i, nn) # insert new structure + + +def findall(node: Element, *args, **kwargs) -> Iterator[Element]: + # findall replaces traverse in docutils v0.18 + # note a difference is that findall is an iterator + impl = getattr(node, "findall", node.traverse) + return iter(impl(*args, **kwargs)) \ No newline at end of file diff --git a/book/_static/code-align.css b/book/_static/code-align.css new file mode 100644 index 0000000..10aa7b4 --- /dev/null +++ b/book/_static/code-align.css @@ -0,0 +1,4 @@ +/* this to enforce left alignment for docutils content (i.e. code cells) */ +div.cell.docutils.container { + text-align: left !important; +} \ No newline at end of file From fddd493e39791f5b2441212a1941ea6065a82563 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 17:18:18 +0100 Subject: [PATCH 02/27] Comment out legacy registry logic in sphinx_gated_directives Commented out the code related to the 'sphinx_gated_directives_registry' in favor of using only the 'sphinx_gated_directives_super_registry'. Updated purge and transform logic accordingly. Added clarifying comments to code-align.css regarding left alignment enforcement. --- book/_ext/sphinx_gated_directives/__init__.py | 105 +++++++++--------- book/_static/code-align.css | 2 + 2 files changed, 54 insertions(+), 53 deletions(-) diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index e3cb8b0..9bb60d8 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -149,27 +149,27 @@ def make_startified_class( # Get environment from state.document.settings env = getattr(self.state.document.settings, "env", None) if env is not None: - # 1) Check whether main registry is already created, if not, create it - if not hasattr(env, "sphinx_gated_directives_registry"): - env.sphinx_gated_directives_registry = {} - # 2) Check whether a sub-registry for current directive type is already created, if not, create it - if orig_name not in env.sphinx_gated_directives_registry: - env.sphinx_gated_directives_registry[orig_name] = {} - # 3) Register current usage in the registry for this doc and this type - gated_registry = env.sphinx_gated_directives_registry[orig_name] docname = env.docname - if docname not in gated_registry: - gated_registry[docname] = { - "start": [], - "end": [], - "sequence": [], - "msg": [], - } - gated_registry[docname]["start"].append(self.lineno) - gated_registry[docname]["sequence"].append("S") - gated_registry[docname]["msg"].append( - f"{self.name} at line: {self.lineno}" - ) + # # 1) Check whether main registry is already created, if not, create it + # if not hasattr(env, "sphinx_gated_directives_registry"): + # env.sphinx_gated_directives_registry = {} + # # 2) Check whether a sub-registry for current directive type is already created, if not, create it + # if orig_name not in env.sphinx_gated_directives_registry: + # env.sphinx_gated_directives_registry[orig_name] = {} + # # 3) Register current usage in the registry for this doc and this type + # gated_registry = env.sphinx_gated_directives_registry[orig_name] + # if docname not in gated_registry: + # gated_registry[docname] = { + # "start": [], + # "end": [], + # "sequence": [], + # "msg": [], + # } + # gated_registry[docname]["start"].append(self.lineno) + # gated_registry[docname]["sequence"].append("S") + # gated_registry[docname]["msg"].append( + # f"{self.name} at line: {self.lineno}" + # ) # 4) Check whether super registry has been created, if not, create it if not hasattr(env, "sphinx_gated_directives_super_registry"): env.sphinx_gated_directives_super_registry = {} @@ -223,27 +223,27 @@ def make_endified_class( # Get environment from state.document.settings env = getattr(self.state.document.settings, "env", None) if env is not None: - # 1) Check whether main registry is already created, if not, create it - if not hasattr(env, "sphinx_gated_directives_registry"): - env.sphinx_gated_directives_registry = {} - # 2) Check whether a sub-registry for current directive type is already created, if not, create it - if orig_name not in env.sphinx_gated_directives_registry: - env.sphinx_gated_directives_registry[orig_name] = {} - # 3) Register current usage in the registry for this doc and this type - gated_registry = env.sphinx_gated_directives_registry[orig_name] docname = env.docname - if docname not in gated_registry: - gated_registry[docname] = { - "start": [], - "end": [], - "sequence": [], - "msg": [], - } - gated_registry[docname]["end"].append(self.lineno) - gated_registry[docname]["sequence"].append("E") - gated_registry[docname]["msg"].append( - f"{self.name} at line: {self.lineno}" - ) + # # 1) Check whether main registry is already created, if not, create it + # if not hasattr(env, "sphinx_gated_directives_registry"): + # env.sphinx_gated_directives_registry = {} + # # 2) Check whether a sub-registry for current directive type is already created, if not, create it + # if orig_name not in env.sphinx_gated_directives_registry: + # env.sphinx_gated_directives_registry[orig_name] = {} + # # 3) Register current usage in the registry for this doc and this type + # gated_registry = env.sphinx_gated_directives_registry[orig_name] + # if docname not in gated_registry: + # gated_registry[docname] = { + # "start": [], + # "end": [], + # "sequence": [], + # "msg": [], + # } + # gated_registry[docname]["end"].append(self.lineno) + # gated_registry[docname]["sequence"].append("E") + # gated_registry[docname]["msg"].append( + # f"{self.name} at line: {self.lineno}" + # ) # 4) Check whether super registry has been created, if not, create it if not hasattr(env, "sphinx_gated_directives_super_registry"): env.sphinx_gated_directives_super_registry = {} @@ -341,17 +341,16 @@ class end_node(nodes.Admonition, nodes.Element): # purge_registry def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: - if hasattr(env, "sphinx_gated_directives_registry") or hasattr(env, "sphinx_gated_directives_super_registry"): - # only perge if needed - if hasattr(env, "sphinx_gated_directives_registry"): - registry = env.sphinx_gated_directives_registry - for directive_name in registry: - if docname in registry[directive_name]: - del registry[directive_name][docname] - if hasattr(env, "sphinx_gated_directives_super_registry"): - super_registry = env.sphinx_gated_directives_super_registry - if docname in super_registry: - del super_registry[docname] + # only perge if needed + # if hasattr(env, "sphinx_gated_directives_registry"): + # registry = env.sphinx_gated_directives_registry + # for directive_name in registry: + # if docname in registry[directive_name]: + # del registry[directive_name][docname] + if hasattr(env, "sphinx_gated_directives_super_registry"): + super_registry = env.sphinx_gated_directives_super_registry + if docname in super_registry: + del super_registry[docname] # Transform to check validity of start-end pairs class CheckGatedDirectivesTransform(SphinxTransform): @@ -359,8 +358,8 @@ class CheckGatedDirectivesTransform(SphinxTransform): def apply(self, **kwargs): env = self.env - if not hasattr(env, "sphinx_gated_directives_registry"): - return + # if not hasattr(env, "sphinx_gated_directives_registry"): + # return if not hasattr(env, "sphinx_gated_directives_super_registry"): return diff --git a/book/_static/code-align.css b/book/_static/code-align.css index 10aa7b4..b9b324a 100644 --- a/book/_static/code-align.css +++ b/book/_static/code-align.css @@ -1,4 +1,6 @@ /* this to enforce left alignment for docutils content (i.e. code cells) */ +/* assumption is that all code is left aligned, so we enforce that */ +/* if not desired, raise an issue with a use case */ div.cell.docutils.container { text-align: left !important; } \ No newline at end of file From bd47d63475b6837e196cdc7fdd491674ea1e3b35 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 17:40:03 +0100 Subject: [PATCH 03/27] Remove debug logging and update intro content Debug logging statements were removed from MergeGatedDirectivesTransform in sphinx_gated_directives. The intro.md file was updated to include new theorem and figure directive examples, code cells, and additional documentation content. --- book/_ext/sphinx_gated_directives/__init__.py | 13 +--- book/intro.md | 62 +++++++++++++++++++ 2 files changed, 63 insertions(+), 12 deletions(-) diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index 9bb60d8..7afe003 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -450,13 +450,6 @@ class MergeGatedDirectivesTransform(SphinxTransform): end_index = parent.children.index(end_n) between_nodes = parent.children[start_index + 1:end_index] - # print the nodes for debugging - logger.info(f"{start_n.pformat()}",color="green") - if between_nodes: - for bn in between_nodes: - logger.info(f"{bn.pformat()}",color="yellow") - logger.info(f"{end_n.pformat()}",color="red") - # We have to merge, but how is the question. # we handle based on "experience" and "observation" of what kind of nodes are generated # step 1: create a new container node to hold the merged content @@ -490,14 +483,10 @@ class MergeGatedDirectivesTransform(SphinxTransform): for i, bn in enumerate(between_nodes): content.insert(pos + i, bn) # insert sets parent/doc links else: - logger.info(f"Found no caption and no section node in start_node content.",color="blue") # otherwise, we add all between nodes to the main content directly\ for bn in between_nodes: content += bn # add sets parent/doc links - logger.info(f"New nodes after merging:",color="blue") - for nn in new_nodes: - logger.info(f"{nn.pformat()}",color="blue") - + # finally, replace start_n with new_nodes, and remove end_n and between_nodes start_pos = parent.children.index(start_n) # remove end_n diff --git a/book/intro.md b/book/intro.md index 165c9f7..5df72ca 100644 --- a/book/intro.md +++ b/book/intro.md @@ -1,5 +1,67 @@ +--- +file_format: mystnb +kernelspec: + name: python3 +--- + # TeachBooks Manual +:::{prf:theorem-start} A title +:label: theorem-label + +This is a theorem. This text is inside the directive. +::: + +Now some extra text that should come inside the theorem. + +:::{figure} images/sphinx-logo.svg +:alt: Sphinx logo +:width: 200px +:align: center + +Sphinx is a great tool for documentation! This figure should be inside the theorem. +::: + +```{code-cell} ipython3 +a = "This is some" +b = "Python code" +c = "that should be inside the theorem." +print(f"{a} {b} {c}") +``` + +:::{prf:theorem-end} +::: + +:::{figure-start} images/logo-wide.svg +:name: figure-label +:alt: JupyterBook logo +:width: 200px +:align: center + +This is a figure. This text is inside the caption. +::: + +```{code-cell} ipython3 +a = "This is some" +b = "Python code" +c = "that should be inside the figure," +d = "above the caption." +print(f"{a} {b} {c} {d}") +``` + +Now some extra text that should come inside the figure, above the caption. + +:::{figure} images/sphinx-logo.svg +:alt: Sphinx logo +:width: 200px +:align: center + +Sphinx is a great tool for documentation! This figure should be inside the parent figure, above the parent caption. +::: + +:::{figure-end} +::: + This manual is primarily designed for and by teachers for use in education, but should be a useful resource for anyone interested in creating and collaborating on a TeachBook: a Jupyter book v1 v1 using the Sphinx logo Sphinx backend with additional editing tools and book features. Our aim is to provide a simple way to start book-making for new users (it only takes 10 clicks!), ease the editing process for anyone, and provide additional features developed for education-purposes. We hope you find this resource useful and refer back to it often. TeachBooks originated at Delft University of Technology in the Netherlands, and some of the material in this Manual reflects tools and resources specific to TU Delft. With the exception of a few tools for which we have educational licenses at Delft, everything in this Manual is open source. Despite this, we are happy to grow our open source community beyond Delft and welcome contributions from all users. From a5f014ddf6ddf75255be771d9bfb39498b4c37cb Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 17:55:31 +0100 Subject: [PATCH 04/27] Replace logo figure with nothing.svg in intro Added a new SVG file 'nothing.svg' and updated the intro.md to use this image in place of the previous logo-wide.svg. Adjusted figure attributes and alt text accordingly. --- book/images/nothing.svg | 1 + book/intro.md | 5 ++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 book/images/nothing.svg diff --git a/book/images/nothing.svg b/book/images/nothing.svg new file mode 100644 index 0000000..2c4a47c --- /dev/null +++ b/book/images/nothing.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/book/intro.md b/book/intro.md index 5df72ca..204b5a3 100644 --- a/book/intro.md +++ b/book/intro.md @@ -32,10 +32,9 @@ print(f"{a} {b} {c}") :::{prf:theorem-end} ::: -:::{figure-start} images/logo-wide.svg +:::{figure-start} images/nothing.svg :name: figure-label -:alt: JupyterBook logo -:width: 200px +:alt: Nothing :align: center This is a figure. This text is inside the caption. From f9e4a0f0b7265975d8b981f2e39fba4ba332833d Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 20:05:44 +0100 Subject: [PATCH 05/27] Improve gated directive nesting validation and docs Added a robust validate_SE function to check for correct nesting of start and end directives, with detailed error reporting. Updated the CheckGatedDirectivesTransform to use this validation and improved error messages for mismatched or incorrectly nested directives. Extended intro.md with comprehensive examples of nested directives, including both valid and invalid cases, to document and test the new behavior. Also added the sphinx_tudelft_theme extension in _config.yml. --- book/_config.yml | 1 + book/_ext/sphinx_gated_directives/__init__.py | 88 +++++++++++-- book/intro.md | 120 ++++++++++++++++++ 3 files changed, 198 insertions(+), 11 deletions(-) diff --git a/book/_config.yml b/book/_config.yml index 580aa76..1e55627 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -19,6 +19,7 @@ sphinx: - teachbooks_favourites - teachbooks_sphinx_grasple - sphinx_apa_references + - sphinx_tudelft_theme config: language: en html_js_files: diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index 7afe003..d213d60 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -30,6 +30,9 @@ from docutils import nodes from docutils.parsers.rst import Directive from docutils.parsers.rst import directives as du_directives +from dataclasses import dataclass, field +from typing import Dict, Optional + SUFFIX_START = "-start" SUFFIX_END = "-end" @@ -142,7 +145,7 @@ def make_startified_class( else: children = [children] # create a start_node and add all result nodes as its children - start_node_instance = start_node() + start_node_instance = start_node(orig_name) start_node_instance += children result = [start_node_instance] @@ -267,7 +270,7 @@ def make_endified_class( # return [] # next line should be good enough, but raises an error as not all has been implemented # maybe we need end nodes per directive type? - return [end_node()] + return [end_node(orig_name)] attrs["run"] = run new_cls_name = f"{base_cls.__name__}_End_For_{orig_name.replace(':', '_')}" @@ -333,10 +336,15 @@ def setup(app): # Node classes for start and end directives class start_node(nodes.Admonition, nodes.Element): - pass + + def __init__(self, type_of_directive: str=""): + self.type_of_directive = type_of_directive + super().__init__() class end_node(nodes.Admonition, nodes.Element): - pass + def __init__(self, type_of_directive: str=""): + self.type_of_directive = type_of_directive + super().__init__() # purge_registry def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: @@ -375,7 +383,7 @@ class CheckGatedDirectivesTransform(SphinxTransform): end = super_registry[docname]["end"] structure = "\n ".join(super_registry[docname]["msg"]) sequence = "".join(super_registry[docname]["sequence"]) - groups = re.findall("(SE)", sequence) + validate_SE_result = validate_SE(sequence) if len(start) > len(end): msg = f"The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." logger.error(msg) @@ -385,21 +393,23 @@ class CheckGatedDirectivesTransform(SphinxTransform): logger.error(msg) error = True # at this point, len(start) == len(end) - elif len(groups) != len(start) or len(groups) != len(end): - msg = f"The document {docname} contains nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." + # now check for correct nesting + elif not validate_SE_result.is_valid: + msg = f"The document {docname} contains incorrectly nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." logger.error(msg) error = True else: - # At this point, every start is followed by an end. + # At this point, every start is matched with an end # Now check that types match in order. types = super_registry[docname]["type"] - start_type = types[::2] - end_type = types[1::2] + start_type = [types[k] for k in validate_SE_result.pairs.keys()] + end_type = [types[v] for v in validate_SE_result.pairs.values()] for i in range(len(start_type)): if start_type[i] != end_type[i]: msg = f"The document {docname} contains mismatched start and end directives at lines {start[i]} and {end[i]}:\n {structure}\nPlease ensure that start and end directives match in type." logger.error(msg) error = True + break if error: raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") @@ -505,4 +515,60 @@ def findall(node: Element, *args, **kwargs) -> Iterator[Element]: # findall replaces traverse in docutils v0.18 # note a difference is that findall is an iterator impl = getattr(node, "findall", node.traverse) - return iter(impl(*args, **kwargs)) \ No newline at end of file + return iter(impl(*args, **kwargs)) + +@dataclass +class SEValidationResult: + is_valid: bool + pairs: Dict[int, int] = field(default_factory=dict) # {S_index: E_index} + error_index: Optional[int] = None # index of first error, if any + error_message: Optional[str] = None # description of the error, if any + +def validate_SE(s: str) -> SEValidationResult: + """ + Validates a string containing only 'S' (open) and 'E' (close). + Returns: + SEValidationResult with: + - is_valid: True iff all brackets match and no invalid characters occur. + - pairs: dict mapping each 'S' index to the corresponding 'E' index. + (zero-based indices in the original string) + - error_index, error_message: populated on first failure. + Errors reported: + * Invalid character (not 'S' or 'E'). + * 'E' encountered before any available 'S'. + * Unmatched 'S' remaining at the end. + The pairs dict is still returned for all successfully matched pairs up to the error. + """ + stack = [] # stack of indices of unmatched 'S' + pairs: Dict[int, int] = {} + + for i, ch in enumerate(s): + if ch == 'S': + stack.append(i) + elif ch == 'E': + if not stack: + return SEValidationResult( + is_valid=False, + pairs=pairs, + error_index=i, + error_message="Encountered 'E' before a matching 'S'." + ) + s_index = stack.pop() + pairs[s_index] = i + else: + return SEValidationResult( + is_valid=False, + pairs=pairs, + error_index=i, + error_message=f"Invalid character: {ch!r}. Only 'S' and 'E' are allowed." + ) + + if stack: + return SEValidationResult( + is_valid=False, + pairs=pairs, + error_index=len(s), + error_message=f"{len(stack)} unmatched 'S' remaining at end." + ) + + return SEValidationResult(is_valid=True, pairs=pairs) diff --git a/book/intro.md b/book/intro.md index 204b5a3..c70a559 100644 --- a/book/intro.md +++ b/book/intro.md @@ -6,6 +6,124 @@ kernelspec: # TeachBooks Manual +## Nesting inside of a start-directive + +### Different directives + +::::{prf:theorem-start} The parent theorem +:label: parent-theorem-label + +This is a parent theorem. This text is inside the parent theorem. + +:::{prf:axiom-start} A child theorem +:label: child-axiom-label + +This is a child axiom. This text is inside the child directive. +::: + +This is text after the child-start, before the child-end, inside the parent theorem. + +:::{prf:axiom-end} +::: + +Finally some text after the entire child, inside the theorem start. + +:::: + +Some text after the entire parent theorem start, but before the parent theorem end. + +:::{prf:theorem-end} +::: + +### Same directives + +::::{prf:theorem-start} The parent theorem +:label: parent-theorem-label2 + +This is a parent theorem. This text is inside the parent theorem. + +:::{prf:theorem-start} A child theorem +:label: child-theorem-label + +This is a child theorem. This text is inside the child directive. +::: + +This is text after the child-start, before the child-end, inside the parent theorem. + +:::{prf:theorem-end} +::: + +Finally some text after the entire child, inside the theorem start. + +:::: + +Some text after the entire parent theorem start, but before the parent theorem end. + +:::{prf:theorem-end} +::: + +## Nesting outside of a start-directive + +### Different directives + +Crashes still: + + + +### Same directives + +Crashes still: + + + +## Examples with code + +### Block directive with also a figure + :::{prf:theorem-start} A title :label: theorem-label @@ -32,6 +150,8 @@ print(f"{a} {b} {c}") :::{prf:theorem-end} ::: +### Figure directive with nothing with also another figure + :::{figure-start} images/nothing.svg :name: figure-label :alt: Nothing From f4815885090b62377c50bf221a27b318dd4240f0 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Fri, 2 Jan 2026 20:08:02 +0100 Subject: [PATCH 06/27] Update child theorem to axiom and add Sphinx theme Changed the label from 'A child theorem' to 'A child axiom' in the intro.md file for clarity. Added 'sphinx-tudelft-theme==1.2.1' to requirements.txt to specify the documentation theme. --- book/intro.md | 2 +- requirements.txt | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/book/intro.md b/book/intro.md index c70a559..c51c487 100644 --- a/book/intro.md +++ b/book/intro.md @@ -15,7 +15,7 @@ kernelspec: This is a parent theorem. This text is inside the parent theorem. -:::{prf:axiom-start} A child theorem +:::{prf:axiom-start} A child axiom :label: child-axiom-label This is a child axiom. This text is inside the child directive. diff --git a/requirements.txt b/requirements.txt index 3b6ef5b..1c93c95 100644 --- a/requirements.txt +++ b/requirements.txt @@ -11,3 +11,5 @@ sphinx-apa-references myst_nb numpy matplotlib + +sphinx-tudelft-theme == 1.2.1 \ No newline at end of file From 144c7a95beed2d739da48c1bd010f072804c7daf Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sat, 3 Jan 2026 10:30:39 +0100 Subject: [PATCH 07/27] Refactor sphinx_gated_directives and update intro.md examples Improved code structure and comments in sphinx_gated_directives/__init__.py, clarified logic, and removed unused/legacy code. Updated intro.md to remove commented-out directive examples and replaced them with active ones for better demonstration and testing. --- book/_ext/sphinx_gated_directives/__init__.py | 257 +++++------------- book/intro.md | 12 +- 2 files changed, 69 insertions(+), 200 deletions(-) diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index d213d60..e956b0b 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -1,24 +1,34 @@ -# startify_directives_all.py """ -Create a '-start' companion for every registered directive (class-based). +~~~~~~~~~~~~~~~~~~~~ -Rules: - - Skip originals whose name already ends with '-start' or '-end'. - - Skip if '-start' already exists. - - Class-based originals -> register a subclass calling original run(). +sphinx_gated_directives + +~~~~~~~~~~~~~~~~~~~~ + +This package is an extension for Sphinx that creates a '-start' companion +and a '-end' companion for every registered class-based directive. + +These new directives can be used to "gate" content in the documentation, +similar to how the gated directives of sphinx-exercise work. + +This extension is based on the extension executablebooks/sphinx-exercise: +https://github.com/executablebooks/sphinx-exercise + +Original project licensed under MIT © Executable Books Developers. +See the repository LICENSE for details. + +Our implementation reuses certain ideas and code snippets (e.g., Docutils +compatibility helpers) to fit the needs of this extension. -Usage: - extensions = ["startify_directives_all"] """ from __future__ import annotations from docutils.nodes import Element from typing import Iterator -import re import copy import importlib import inspect -from typing import Callable, Dict, Iterable, List, Optional +from typing import Dict, Iterable, Optional from sphinx.transforms import SphinxTransform from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment @@ -31,34 +41,14 @@ from docutils.parsers.rst import Directive from docutils.parsers.rst import directives as du_directives from dataclasses import dataclass, field -from typing import Dict, Optional SUFFIX_START = "-start" SUFFIX_END = "-end" - -# --------------------------- -# Helpers: identification -# --------------------------- - def _is_class_directive(obj) -> bool: return inspect.isclass(obj) and issubclass(obj, Directive) - -# --------------------------- -# Helpers: registry access -# --------------------------- - def _get_unified_registry(app=None) -> Dict[str, object]: - """ - Build a unified {name -> implementation} mapping of directives. - - Sources: - 1) docutils.parsers.rst.directives._directives (loaded/added so far) - 2) docutils.parsers.rst.directives._directive_registry (lazy map of - name -> (module, class) for builtins); import any missing ones. - 3) Sphinx app.registry (if provided) for extension-registered directives - """ # 1) Already-loaded directives (includes Sphinx & extensions registered via add_directive) unified: Dict[str, object] = dict(getattr(du_directives, "_directives", {})) @@ -104,21 +94,11 @@ def _get_unified_registry(app=None) -> Dict[str, object]: return unified - -# --------------------------- -# Helpers: class generation -# --------------------------- - def _copy_option_spec(option_spec): return copy.copy(option_spec) if isinstance(option_spec, dict) else option_spec -def make_startified_class( - orig_name: str, - base_cls: type[Directive], -) -> type[Directive]: - """ - Subclass `base_cls` to call original run(). - """ +def make_start_class(orig_name: str, base_cls: type[Directive]) -> type[Directive]: + attrs = {} for attr in ( "required_arguments", @@ -145,7 +125,7 @@ def make_startified_class( else: children = [children] # create a start_node and add all result nodes as its children - start_node_instance = start_node(orig_name) + start_node_instance = start_node() start_node_instance += children result = [start_node_instance] @@ -153,30 +133,10 @@ def make_startified_class( env = getattr(self.state.document.settings, "env", None) if env is not None: docname = env.docname - # # 1) Check whether main registry is already created, if not, create it - # if not hasattr(env, "sphinx_gated_directives_registry"): - # env.sphinx_gated_directives_registry = {} - # # 2) Check whether a sub-registry for current directive type is already created, if not, create it - # if orig_name not in env.sphinx_gated_directives_registry: - # env.sphinx_gated_directives_registry[orig_name] = {} - # # 3) Register current usage in the registry for this doc and this type - # gated_registry = env.sphinx_gated_directives_registry[orig_name] - # if docname not in gated_registry: - # gated_registry[docname] = { - # "start": [], - # "end": [], - # "sequence": [], - # "msg": [], - # } - # gated_registry[docname]["start"].append(self.lineno) - # gated_registry[docname]["sequence"].append("S") - # gated_registry[docname]["msg"].append( - # f"{self.name} at line: {self.lineno}" - # ) - # 4) Check whether super registry has been created, if not, create it + # 1) Check whether super registry has been created, if not, create it if not hasattr(env, "sphinx_gated_directives_super_registry"): env.sphinx_gated_directives_super_registry = {} - # 5) Register current usage in the super registry + # 2) Register current usage in the super registry super_registry = env.sphinx_gated_directives_super_registry if docname not in super_registry: super_registry[docname] = { @@ -199,13 +159,8 @@ def make_startified_class( new_cls_name = f"{base_cls.__name__}_Start_For_{orig_name.replace(':', '_')}" return type(new_cls_name, (base_cls,), attrs) -def make_endified_class( - orig_name: str, - base_cls: type[Directive], -) -> type[Directive]: - """ - Just create a subclass to obtain an end node. - """ +def make_end_class(orig_name: str, base_cls: type[Directive]) -> type[Directive]: + attrs = {} for attr in ( "required_arguments", @@ -227,30 +182,10 @@ def make_endified_class( env = getattr(self.state.document.settings, "env", None) if env is not None: docname = env.docname - # # 1) Check whether main registry is already created, if not, create it - # if not hasattr(env, "sphinx_gated_directives_registry"): - # env.sphinx_gated_directives_registry = {} - # # 2) Check whether a sub-registry for current directive type is already created, if not, create it - # if orig_name not in env.sphinx_gated_directives_registry: - # env.sphinx_gated_directives_registry[orig_name] = {} - # # 3) Register current usage in the registry for this doc and this type - # gated_registry = env.sphinx_gated_directives_registry[orig_name] - # if docname not in gated_registry: - # gated_registry[docname] = { - # "start": [], - # "end": [], - # "sequence": [], - # "msg": [], - # } - # gated_registry[docname]["end"].append(self.lineno) - # gated_registry[docname]["sequence"].append("E") - # gated_registry[docname]["msg"].append( - # f"{self.name} at line: {self.lineno}" - # ) - # 4) Check whether super registry has been created, if not, create it + # 1) Check whether super registry has been created, if not, create it if not hasattr(env, "sphinx_gated_directives_super_registry"): env.sphinx_gated_directives_super_registry = {} - # 5) Register current usage in the super registry + # 2) Register current usage in the super registry super_registry = env.sphinx_gated_directives_super_registry if docname not in super_registry: super_registry[docname] = { @@ -267,23 +202,16 @@ def make_endified_class( ) super_registry[docname]["type"].append(orig_name) - # return [] - # next line should be good enough, but raises an error as not all has been implemented - # maybe we need end nodes per directive type? - return [end_node(orig_name)] + return [end_node()] attrs["run"] = run new_cls_name = f"{base_cls.__name__}_End_For_{orig_name.replace(':', '_')}" return type(new_cls_name, (base_cls,), attrs) -# --------------------------- -# Core logic -# --------------------------- - def _should_skip_name(orig_name: str) -> bool: return orig_name.endswith(SUFFIX_START) or orig_name.endswith(SUFFIX_END) -def _register_startified_directives(app, env, docnames): +def _register_new_directives(app, env, docnames): unified = _get_unified_registry(app) snapshot_names = set(unified.keys()) added = 0 @@ -298,27 +226,26 @@ def _register_startified_directives(app, env, docnames): try: if _is_class_directive(obj): - start_cls = make_startified_class(orig_name, obj) + start_cls = make_start_class(orig_name, obj) # new class based on original so all properties are inherited app.add_directive(new_name, start_cls) - end_cls = make_endified_class(orig_name, Directive) # because it does generate anything, most simple directive + end_cls = make_end_class(orig_name, Directive) # because it does not generate anything, most simple directive possible app.add_directive(f"{orig_name}{SUFFIX_END}", end_cls) added += 1 else: - logger.debug(f"[startify] '{orig_name}' is not a class directive; skipping.") + logger.debug(f"[sphinx-gated-directives] '{orig_name}' is not a class directive; skipping.") except Exception as e: - logger.warning(f"[startify] failed to register '{new_name}': {e}") + logger.warning(f"[sphinx-gated-directives] failed to register new classes for directive '{orig_name}':\n{e}") def setup(app): - # register function to perge registries at start of build per document + # register function to purge registries at start of build per document app.connect("env-purge-doc", purge_registries) - # At the latest possible moment, register startified directives (also creates the endified ones) - app.connect("env-before-read-docs", _register_startified_directives,priority=10000000000000000000000000000) + # At the latest possible moment, register new directives + app.connect("env-before-read-docs", _register_new_directives,priority=10000000000000000000000000000) - # Register nodes, used as placeholder for end directives - # will be resolved in the transforms to something meaningful - # during merging of start-end blocks + # Register nodes + # During transformation phase, these nodes will be removed app.add_node(start_node) app.add_node(end_node) @@ -329,32 +256,17 @@ def setup(app): app.add_transform(MergeGatedDirectivesTransform) return { - "version": "1.0", "parallel_read_safe": True, } -# Node classes for start and end directives - class start_node(nodes.Admonition, nodes.Element): - - def __init__(self, type_of_directive: str=""): - self.type_of_directive = type_of_directive - super().__init__() + pass class end_node(nodes.Admonition, nodes.Element): - def __init__(self, type_of_directive: str=""): - self.type_of_directive = type_of_directive - super().__init__() + pass -# purge_registry def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: - # only perge if needed - # if hasattr(env, "sphinx_gated_directives_registry"): - # registry = env.sphinx_gated_directives_registry - # for directive_name in registry: - # if docname in registry[directive_name]: - # del registry[directive_name][docname] if hasattr(env, "sphinx_gated_directives_super_registry"): super_registry = env.sphinx_gated_directives_super_registry if docname in super_registry: @@ -366,15 +278,9 @@ class CheckGatedDirectivesTransform(SphinxTransform): def apply(self, **kwargs): env = self.env - # if not hasattr(env, "sphinx_gated_directives_registry"): - # return if not hasattr(env, "sphinx_gated_directives_super_registry"): return - # First check the super registry for a correct structure - # demands: - # 1) equal number of start and end directives - # 2) no nesting of start-end pairs (so SSEE is not allowed, only SESE) super_registry = env.sphinx_gated_directives_super_registry error = False docname = self.env.docname @@ -385,17 +291,17 @@ class CheckGatedDirectivesTransform(SphinxTransform): sequence = "".join(super_registry[docname]["sequence"]) validate_SE_result = validate_SE(sequence) if len(start) > len(end): - msg = f"The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." + msg = f"[sphinx-gated-directives] The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." logger.error(msg) error = True elif len(end) > len(start): - msg = f"The document {docname} contains more end directives than start directives:\n {structure}\nPlease ensure each end directive has a corresponding start directive." + msg = f"[sphinx-gated-directives] The document {docname} contains more end directives than start directives:\n {structure}\nPlease ensure each end directive has a corresponding start directive." logger.error(msg) error = True # at this point, len(start) == len(end) # now check for correct nesting elif not validate_SE_result.is_valid: - msg = f"The document {docname} contains incorrectly nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." + msg = f"[sphinx-gated-directives] The document {docname} contains incorrectly nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." logger.error(msg) error = True else: @@ -406,7 +312,7 @@ class CheckGatedDirectivesTransform(SphinxTransform): end_type = [types[v] for v in validate_SE_result.pairs.values()] for i in range(len(start_type)): if start_type[i] != end_type[i]: - msg = f"The document {docname} contains mismatched start and end directives at lines {start[i]} and {end[i]}:\n {structure}\nPlease ensure that start and end directives match in type." + msg = f"[sphinx-gated-directives] The document {docname} contains mismatched start and end directives at lines {start[i]} and {end[i]}:\n {structure}\nPlease ensure that start and end directives match in type." logger.error(msg) error = True break @@ -414,22 +320,11 @@ class CheckGatedDirectivesTransform(SphinxTransform): if error: raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") - # no check per directive type is needed if no error in super registry is raised, - # as the super registry already ensures correctness for each type. - # if nesting is allowed in the future, this part needs to be re-implemented. - - class MergeGatedDirectivesTransform(SphinxTransform): default_priority = 10 def apply(self, **kwargs): env = self.env - # Something to do here - # idea: - # 1) Find all start_nodes - # 2) for each start_node, find the corresponding end_node (i.e. next end_node) - # 3) collect all nodes in between - # 4) Take the first node after start_node, and add all other collected nodes as its children if not hasattr(env, "sphinx_gated_directives_super_registry"): return super_registry = env.sphinx_gated_directives_super_registry @@ -437,80 +332,72 @@ class MergeGatedDirectivesTransform(SphinxTransform): if docname not in super_registry: return - # Each start-end pair is valid (checked before), so we can proceed to merge - # find all start nodes, as an iterator - # for each start node, find the next end node within the parent of start node start_nodes = findall(self.document, start_node) for start_n in start_nodes: parent = start_n.parent - # find the next end_node after start_n in parent found_start = False end_n = None + skip_end = 0 for child in parent.children: if child is start_n: found_start = True - elif found_start and isinstance(child, end_node): + elif found_start and isinstance(child, end_node) and skip_end==0: end_n = child break + elif found_start and isinstance(child, end_node): + skip_end -= 1 + elif found_start and isinstance(child, start_node): + skip_end += 1 if end_n is None: - continue # should not happen due to prior checks + logger.error(f"[sphinx-gated-directives] Could not find matching end directive for start directive at line {start_n.line} in document {docname}. Skipping merging for this pair.") + raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") - # collect all nodes between start_n and end_n start_index = parent.children.index(start_n) end_index = parent.children.index(end_n) between_nodes = parent.children[start_index + 1:end_index] - # We have to merge, but how is the question. - # we handle based on "experience" and "observation" of what kind of nodes are generated - # step 1: create a new container node to hold the merged content - # content of start_node new_nodes = start_n.children - # add content in between, depending on content in start_n if between_nodes: has_section = False has_caption = False si = -1 - content = new_nodes[-1] # we assume that the last child of start_n holds the main content + content = new_nodes[-1] for sn in content.children: si += 1 if isinstance(sn, nodes.section): - has_section = True # So probably a topic or similar (from sphinx-proof) + has_section = True section_index = si section_node = sn break elif isinstance(sn, nodes.caption): - has_caption = True # So probably a figure or similar + has_caption = True caption_index = si caption_node = sn break if has_section: - # if there are sections, we add all between nodes to this section content[section_index] += between_nodes elif has_caption: - # if there is a caption, we add all between nodes before the caption n = len(content.children) pos = n - 1 for i, bn in enumerate(between_nodes): - content.insert(pos + i, bn) # insert sets parent/doc links + content.insert(pos + i, bn) else: - # otherwise, we add all between nodes to the main content directly\ for bn in between_nodes: - content += bn # add sets parent/doc links + content += bn - # finally, replace start_n with new_nodes, and remove end_n and between_nodes start_pos = parent.children.index(start_n) - # remove end_n parent.remove(end_n) - # remove start_n parent.remove(start_n) - # remove between_nodes for bn in between_nodes: parent.remove(bn) - # insert new_nodes at start_pos for i, nn in enumerate(new_nodes): - parent.insert(start_pos + i, nn) # insert new structure + parent.insert(start_pos + i, nn) +# SPDX-License-Identifier: MIT +# Adapted from: executablebooks/sphinx-exercise, file: sphinx_exercise/_compat.py +# Source: https://github.com/executablebooks/sphinx-exercise/blob/main/sphinx_exercise/_compat.py +# License: MIT — © Executable Books Developers (see repository LICENSE). def findall(node: Element, *args, **kwargs) -> Iterator[Element]: # findall replaces traverse in docutils v0.18 # note a difference is that findall is an iterator @@ -525,21 +412,7 @@ class SEValidationResult: error_message: Optional[str] = None # description of the error, if any def validate_SE(s: str) -> SEValidationResult: - """ - Validates a string containing only 'S' (open) and 'E' (close). - Returns: - SEValidationResult with: - - is_valid: True iff all brackets match and no invalid characters occur. - - pairs: dict mapping each 'S' index to the corresponding 'E' index. - (zero-based indices in the original string) - - error_index, error_message: populated on first failure. - Errors reported: - * Invalid character (not 'S' or 'E'). - * 'E' encountered before any available 'S'. - * Unmatched 'S' remaining at the end. - The pairs dict is still returned for all successfully matched pairs up to the error. - """ - stack = [] # stack of indices of unmatched 'S' + stack = [] pairs: Dict[int, int] = {} for i, ch in enumerate(s): diff --git a/book/intro.md b/book/intro.md index c51c487..3a415f8 100644 --- a/book/intro.md +++ b/book/intro.md @@ -66,9 +66,7 @@ Some text after the entire parent theorem start, but before the parent theorem e ### Different directives -Crashes still: - - +::: ### Same directives -Crashes still: - - +::: ## Examples with code From f4ea60d42c4af8378e5e778a3a31d3b0365de38e Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sat, 3 Jan 2026 12:45:24 +0100 Subject: [PATCH 08/27] Enhance sphinx_gated_directives with configurable suffixes and override logic Refactor the sphinx_gated_directives extension to support configurable suffixes and separator for start/end directives, and add an 'override_existing' option for more flexible directive registration. Update the registry naming and logic throughout the extension, add configuration validation, and update the book config and intro to demonstrate new exercise directives and nesting. --- book/_config.yml | 2 + book/_ext/sphinx_gated_directives/__init__.py | 176 +++++++++++++----- book/intro.md | 35 +++- 3 files changed, 167 insertions(+), 46 deletions(-) diff --git a/book/_config.yml b/book/_config.yml index 1e55627..8f88cff 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -21,6 +21,8 @@ sphinx: - sphinx_apa_references - sphinx_tudelft_theme config: + sphinx_gated_directives: + override_existing: ['exercise'] language: en html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index e956b0b..c650d97 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -42,8 +42,9 @@ from docutils.parsers.rst import directives as du_directives from dataclasses import dataclass, field -SUFFIX_START = "-start" -SUFFIX_END = "-end" +SUFFIX_START = "start" +SUFFIX_END = "end" +SUFFIX_SEPARATOR = "-" def _is_class_directive(obj) -> bool: return inspect.isclass(obj) and issubclass(obj, Directive) @@ -134,24 +135,24 @@ def make_start_class(orig_name: str, base_cls: type[Directive]) -> type[Directiv if env is not None: docname = env.docname # 1) Check whether super registry has been created, if not, create it - if not hasattr(env, "sphinx_gated_directives_super_registry"): - env.sphinx_gated_directives_super_registry = {} + if not hasattr(env, "sphinx_gated_directives_registry"): + env.sphinx_gated_directives_registry = {} # 2) Register current usage in the super registry - super_registry = env.sphinx_gated_directives_super_registry - if docname not in super_registry: - super_registry[docname] = { + registry = env.sphinx_gated_directives_registry + if docname not in registry: + registry[docname] = { "start": [], "end": [], "sequence": [], "msg": [], "type": [], } - super_registry[docname]["start"].append(self.lineno) - super_registry[docname]["sequence"].append("S") - super_registry[docname]["msg"].append( + registry[docname]["start"].append(self.lineno) + registry[docname]["sequence"].append("S") + registry[docname]["msg"].append( f"{self.name} at line: {self.lineno}" ) - super_registry[docname]["type"].append(orig_name) + registry[docname]["type"].append(orig_name) return result @@ -183,24 +184,24 @@ def make_end_class(orig_name: str, base_cls: type[Directive]) -> type[Directive] if env is not None: docname = env.docname # 1) Check whether super registry has been created, if not, create it - if not hasattr(env, "sphinx_gated_directives_super_registry"): - env.sphinx_gated_directives_super_registry = {} + if not hasattr(env, "sphinx_gated_directives_registry"): + env.sphinx_gated_directives_registry = {} # 2) Register current usage in the super registry - super_registry = env.sphinx_gated_directives_super_registry - if docname not in super_registry: - super_registry[docname] = { + registry = env.sphinx_gated_directives_registry + if docname not in registry: + registry[docname] = { "start": [], "end": [], "sequence": [], "msg": [], "type": [], } - super_registry[docname]["end"].append(self.lineno) - super_registry[docname]["sequence"].append("E") - super_registry[docname]["msg"].append( + registry[docname]["end"].append(self.lineno) + registry[docname]["sequence"].append("E") + registry[docname]["msg"].append( f"{self.name} at line: {self.lineno}" ) - super_registry[docname]["type"].append(orig_name) + registry[docname]["type"].append(orig_name) return [end_node()] @@ -208,28 +209,73 @@ def make_end_class(orig_name: str, base_cls: type[Directive]) -> type[Directive] new_cls_name = f"{base_cls.__name__}_End_For_{orig_name.replace(':', '_')}" return type(new_cls_name, (base_cls,), attrs) -def _should_skip_name(orig_name: str) -> bool: - return orig_name.endswith(SUFFIX_START) or orig_name.endswith(SUFFIX_END) +def _should_skip_name(orig_name: str, cfg: dict) -> bool: + if isinstance(cfg["override_existing"],bool): + if cfg["override_existing"] is True: + return False + else: + if orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_start"]}"): + return True + else: + return orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_end"]}") + elif isinstance(cfg["override_existing"],list): + if orig_name in cfg["override_existing"]: + return False + else: + if orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_start"]}"): + return True + else: + return orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_end"]}") def _register_new_directives(app, env, docnames): + + # get options and add missing default values + cfg = app.config.sphinx_gated_directives + if "override_existing" not in cfg: + cfg["override_existing"] = False + elif isinstance(cfg["override_existing"], str): + cfg["override_existing"] = [cfg["override_existing"]] + if "suffix_start" not in cfg: + cfg["suffix_start"] = SUFFIX_START + if "suffix_end" not in cfg: + cfg["suffix_end"] = SUFFIX_END + if "suffix_separator" not in cfg: + cfg["suffix_separator"] = SUFFIX_SEPARATOR + unified = _get_unified_registry(app) snapshot_names = set(unified.keys()) added = 0 for orig_name, obj in sorted(unified.items()): - if _should_skip_name(orig_name): - continue - new_name = f"{orig_name}{SUFFIX_START}" + new_name = f"{orig_name}{cfg["suffix_separator"]}{cfg["suffix_start"]}" + # add start and end directives if not already present, or if override is requested explicitly if new_name in snapshot_names: + if isinstance(cfg["override_existing"], bool): + if cfg["override_existing"] is False: + continue + else: + if orig_name not in cfg["override_existing"]: + continue + end_name = f"{orig_name}{cfg["suffix_separator"]}{cfg["suffix_end"]}" + if end_name in snapshot_names: + if isinstance(cfg["override_existing"], bool): + if cfg["override_existing"] is False: + continue + else: + if orig_name not in cfg["override_existing"]: + continue + + # if the original name is itself a start or end directive, skip + if _should_skip_name(orig_name,cfg): continue try: if _is_class_directive(obj): start_cls = make_start_class(orig_name, obj) # new class based on original so all properties are inherited - app.add_directive(new_name, start_cls) + app.add_directive(new_name, start_cls,override=True) end_cls = make_end_class(orig_name, Directive) # because it does not generate anything, most simple directive possible - app.add_directive(f"{orig_name}{SUFFIX_END}", end_cls) + app.add_directive(end_name, end_cls,override=True) added += 1 else: logger.debug(f"[sphinx-gated-directives] '{orig_name}' is not a class directive; skipping.") @@ -237,6 +283,11 @@ def _register_new_directives(app, env, docnames): logger.warning(f"[sphinx-gated-directives] failed to register new classes for directive '{orig_name}':\n{e}") def setup(app): + # Register a config value to set options for the extension + app.add_config_value("sphinx_gated_directives", {}, "env") + + # Register a function to check if config value structure is correct + app.connect("config-inited", check_config_values) # register function to purge registries at start of build per document app.connect("env-purge-doc", purge_registries) @@ -267,10 +318,10 @@ class end_node(nodes.Admonition, nodes.Element): def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: - if hasattr(env, "sphinx_gated_directives_super_registry"): - super_registry = env.sphinx_gated_directives_super_registry - if docname in super_registry: - del super_registry[docname] + if hasattr(env, "sphinx_gated_directives_registry"): + registry = env.sphinx_gated_directives_registry + if docname in registry: + del registry[docname] # Transform to check validity of start-end pairs class CheckGatedDirectivesTransform(SphinxTransform): @@ -278,17 +329,17 @@ class CheckGatedDirectivesTransform(SphinxTransform): def apply(self, **kwargs): env = self.env - if not hasattr(env, "sphinx_gated_directives_super_registry"): + if not hasattr(env, "sphinx_gated_directives_registry"): return - super_registry = env.sphinx_gated_directives_super_registry + registry = env.sphinx_gated_directives_registry error = False docname = self.env.docname - if docname in super_registry: - start = super_registry[docname]["start"] - end = super_registry[docname]["end"] - structure = "\n ".join(super_registry[docname]["msg"]) - sequence = "".join(super_registry[docname]["sequence"]) + if docname in registry: + start = registry[docname]["start"] + end = registry[docname]["end"] + structure = "\n ".join(registry[docname]["msg"]) + sequence = "".join(registry[docname]["sequence"]) validate_SE_result = validate_SE(sequence) if len(start) > len(end): msg = f"[sphinx-gated-directives] The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." @@ -307,7 +358,7 @@ class CheckGatedDirectivesTransform(SphinxTransform): else: # At this point, every start is matched with an end # Now check that types match in order. - types = super_registry[docname]["type"] + types = registry[docname]["type"] start_type = [types[k] for k in validate_SE_result.pairs.keys()] end_type = [types[v] for v in validate_SE_result.pairs.values()] for i in range(len(start_type)): @@ -325,11 +376,11 @@ class MergeGatedDirectivesTransform(SphinxTransform): def apply(self, **kwargs): env = self.env - if not hasattr(env, "sphinx_gated_directives_super_registry"): + if not hasattr(env, "sphinx_gated_directives_registry"): return - super_registry = env.sphinx_gated_directives_super_registry + registry = env.sphinx_gated_directives_registry docname = self.env.docname - if docname not in super_registry: + if docname not in registry: return start_nodes = findall(self.document, start_node) @@ -445,3 +496,44 @@ def validate_SE(s: str) -> SEValidationResult: ) return SEValidationResult(is_valid=True, pairs=pairs) + +def check_config_values(app: Sphinx, config) -> None: + cfg = config.sphinx_gated_directives + if not isinstance(cfg, dict): + logger.error("[sphinx-gated-directives] The configuration value 'sphinx_gated_directives' should be a dictionary.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives'. Expected a dictionary.") + return + # Implemented options: + # - override_existing: bool (default: False), string or list of strings + override_existing = cfg.get("override_existing", False) + if not (isinstance(override_existing, bool) or isinstance(override_existing, str) or (isinstance(override_existing, list) and all(isinstance(x, str) for x in override_existing))): + logger.error("[sphinx-gated-directives] The configuration option 'override_existing' should be a boolean, a string, or a list of strings.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.override_existing'. Expected a boolean, string, or list of strings.") + # - suffix_start: string (default: 'start'), only a-z allowed, or empty string + suffix_start = cfg.get("suffix_start", SUFFIX_START) + if not isinstance(suffix_start, str): + logger.error("[sphinx-gated-directives] The configuration option 'suffix_start' should be a string.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_start'. Expected a string.") + if suffix_start != "" and (not suffix_start.islower() or not suffix_start.isalpha()): + logger.error("[sphinx-gated-directives] The configuration option 'suffix_start' should contain only lowercase letters (a-z).") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_start'. Expected only lowercase letters (a-z).") + # - suffix_end: string (default: 'end'), only a-z allowed, or empty string + suffix_end = cfg.get("suffix_end", SUFFIX_END) + if not isinstance(suffix_end, str): + logger.error("[sphinx-gated-directives] The configuration option 'suffix_end' should be a string.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_end'. Expected a string.") + if suffix_end != "" and (not suffix_end.islower() or not suffix_end.isalpha()): + logger.error("[sphinx-gated-directives] The configuration option 'suffix_end' should contain only lowercase letters (a-z).") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_end'. Expected only lowercase letters (a-z).") + # - suffix_separator: string (default: '-'), single character, no space, no underscore, no colon, or empty string + suffix_separator = cfg.get("suffix_separator", SUFFIX_SEPARATOR) + if not isinstance(suffix_separator, str): + logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should be a string.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected a string.") + if len(suffix_separator) > 1: + logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should be a single character or an empty string.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected a single character or an empty string.") + if suffix_separator in [' ', '_',':']: + logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should not be a space, nor an underscore, nor a colon.") + raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected something other than a space, an underscore, or a colon.") + \ No newline at end of file diff --git a/book/intro.md b/book/intro.md index 3a415f8..4df190b 100644 --- a/book/intro.md +++ b/book/intro.md @@ -74,15 +74,15 @@ This is a parent theorem. This text is inside the parent theorem. Some text outside the parent theorem start, before the child start directive. -:::{prf:axiom-start} A child theorem -:label: child-axiom-label2 +:::{exercise-start} A child exercise +:label: child-exercise-label2 -This is a child axiom. This text is inside the child directive. +This is a child exercise. This text is inside the child directive. ::: This is text after the child-start, before the child-end. -:::{prf:axiom-end} +:::{exercise-end} ::: Finally some text after the entire child, outside the theorem start, before the end. @@ -90,6 +90,12 @@ Finally some text after the entire child, outside the theorem start, before the :::{prf:theorem-end} ::: +:::{exercise} This is an exercise outside any theorem. +:label: exercise-outside-theorem + +Solve this exercise. +::: + ### Same directives ::::{prf:theorem-start} The parent theorem @@ -116,6 +122,27 @@ Finally some text after the entire child, outside the theorem start, before the :::{prf:theorem-end} ::: +### Nested exercise directives + +:::{exercise-start} An outer exercise with an inner exercise between start and end +:label: outer-exercise-label +This is an outer exercise. This text is inside the outer exercise. +::: + +:::{exercise-start} An inner exercise +:label: inner-exercise-label +This is an inner exercise. This text is inside the inner exercise. +::: +This is text after the inner exercise start, before the inner exercise end. + +:::{exercise-end} +::: + +Finally some text after the entire inner exercise, inside the outer exercise. + +:::{exercise-end} +::: + ## Examples with code ### Block directive with also a figure From e12183af9eee6338e765aa6e3bdb7a0e19ac4025 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sat, 3 Jan 2026 12:48:34 +0100 Subject: [PATCH 09/27] Refactor _should_skip_name for clarity and efficiency Extracted suffix_separator, suffix_start, and suffix_end from cfg at the start of the function to avoid repeated dictionary lookups and improve code readability. --- book/_ext/sphinx_gated_directives/__init__.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index c650d97..f85538a 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -210,22 +210,25 @@ def make_end_class(orig_name: str, base_cls: type[Directive]) -> type[Directive] return type(new_cls_name, (base_cls,), attrs) def _should_skip_name(orig_name: str, cfg: dict) -> bool: + suffix_separator = cfg["suffix_separator"] + suffix_start = cfg["suffix_start"] + suffix_end = cfg["suffix_end"] if isinstance(cfg["override_existing"],bool): if cfg["override_existing"] is True: return False else: - if orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_start"]}"): + if orig_name.endswith(f"{suffix_separator}{suffix_start}"): return True else: - return orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_end"]}") + return orig_name.endswith(f"{suffix_separator}{suffix_end}") elif isinstance(cfg["override_existing"],list): if orig_name in cfg["override_existing"]: return False else: - if orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_start"]}"): + if orig_name.endswith(f"{suffix_separator}{suffix_start}"): return True else: - return orig_name.endswith(f"{cfg["suffix_separator"]}{cfg["suffix_end"]}") + return orig_name.endswith(f"{suffix_separator}{suffix_end}") def _register_new_directives(app, env, docnames): From 1ac94844a33e1a9ee12a7e956c01e9fb0900d385 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sat, 3 Jan 2026 12:50:11 +0100 Subject: [PATCH 10/27] Refactor directive name construction for clarity Extracted suffix_separator, suffix_start, and suffix_end from cfg to local variables for improved readability and maintainability in directive name construction. --- book/_ext/sphinx_gated_directives/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py index f85538a..5aef914 100644 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ b/book/_ext/sphinx_gated_directives/__init__.py @@ -249,9 +249,12 @@ def _register_new_directives(app, env, docnames): snapshot_names = set(unified.keys()) added = 0 + suffix_separator = cfg["suffix_separator"] + suffix_start = cfg["suffix_start"] + suffix_end = cfg["suffix_end"] for orig_name, obj in sorted(unified.items()): - new_name = f"{orig_name}{cfg["suffix_separator"]}{cfg["suffix_start"]}" + new_name = f"{orig_name}{suffix_separator}{suffix_start}" # add start and end directives if not already present, or if override is requested explicitly if new_name in snapshot_names: if isinstance(cfg["override_existing"], bool): @@ -260,7 +263,7 @@ def _register_new_directives(app, env, docnames): else: if orig_name not in cfg["override_existing"]: continue - end_name = f"{orig_name}{cfg["suffix_separator"]}{cfg["suffix_end"]}" + end_name = f"{orig_name}{suffix_separator}{suffix_end}" if end_name in snapshot_names: if isinstance(cfg["override_existing"], bool): if cfg["override_existing"] is False: From e22ea50d6d57bcdf744ee0b6083293c7027024df Mon Sep 17 00:00:00 2001 From: Tom van Woudenberg Date: Sat, 3 Jan 2026 15:35:40 +0100 Subject: [PATCH 11/27] added gates --- book/_config.yml | 2 + book/_toc.yml | 5 + book/instructie.ipynb | 1470 ++++++++++++++++++++++++++++++++++++++ book/instructie_2.ipynb | 1470 ++++++++++++++++++++++++++++++++++++++ book/lesoefeningen.ipynb | 316 ++++++++ 5 files changed, 3263 insertions(+) create mode 100644 book/instructie.ipynb create mode 100644 book/instructie_2.ipynb create mode 100644 book/lesoefeningen.ipynb diff --git a/book/_config.yml b/book/_config.yml index 8f88cff..ab31891 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -57,6 +57,8 @@ sphinx: summaries: false individual: false nb_execution_includepatterns: "**/MANUAL.ipynb" + sphinx_codex_name: "Voorbeeld" + sphinx_codex_merge_with_proof: true # default value teachbooks: attribution_location: margin diff --git a/book/_toc.yml b/book/_toc.yml index 4fc09d5..561237f 100644 --- a/book/_toc.yml +++ b/book/_toc.yml @@ -2,6 +2,11 @@ format: jb-book root: intro parts: + - caption: test sphinx_gated_directives + chapters: + - file: instructie.ipynb + - file: instructie_2.ipynb + - file: lesoefeningen.ipynb - caption: Your First TeachBook! chapters: - file: intro/book.md diff --git a/book/instructie.ipynb b/book/instructie.ipynb new file mode 100644 index 0000000..e2ec472 --- /dev/null +++ b/book/instructie.ipynb @@ -0,0 +1,1470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "````{margin}\n", + "```{attributiongrey} Attribution\n", + ":class: attribution\n", + "\n", + "Deze pagina is aangepast van https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro.ipynb\n", + "\n", + "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro_data/Tekening1.vsdx\n", + "\n", + "```\n", + "```` " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Instructie_oud\n", + "\n", + "In constructiemechanica 2 heb je geleerd hoe je met differentiaalvergelijkingen voor statisch bepaalde constructies de krachtsverdeling en vervormingen kan vinden. Precies dezelfde aanpak werkt ook voor statisch onbepaalde constructie, met als enige verschil dat we de inwendige krachten niet kunnen vinden zonder ook de vergelijkingen en randvoorwaarden voor verplaatsingen op te stellen. Dat doen we volgens de volgende stappen:\n", + "\n", + "::::::{prf:algorithm} Differentiaalvergelijkingen toepassen\n", + ":nonumber: true\n", + ":label: differentiaalvergelijkingen_algoritme\n", + "\n", + "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", + "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", + "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", + "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", + "\n", + "::::::\n", + "\n", + "Als voorbeeld bepalen we de een simpele statisch onbepaalde constructie:\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_0\n", + "\n", + "Gegeven een constructie zoals hieronder weergegeven:\n", + "\n", + "```{figure} intro_data/structure.svg\n", + "---\n", + "align: center\n", + "---\n", + "Constructie, $EI = 5000 \\ \\rm{kNm^2}$\n", + "```\n", + "::::::\n", + "\n", + "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_1\n", + "\n", + "In dit voorbeeld moeten we element AB in twee delen opsplitsen omdat de verdeelde belasting een discontinuïteit veroorzaakt.\n", + "\n", + "Voor AC leidt dit tot:\n", + "\n", + "```{figure} intro_data/FBD_AC.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram element AC\n", + "```\n", + "\n", + "Voor BC leidt dit tot:\n", + "\n", + "```{figure} intro_data/FBD_BC.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram element BC\n", + "```\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_3\n", + "\n", + "In dit voorbeeld is er enkel invloed door buiging, dus hebben we aan deze differentiaalvergelijking genoeg: $ -EI \\cfrac{{d}^4w}{{d}x^4} = -q_z$. De differentiaalvergelijkingen voor rek $ EA \\cfrac{{d}^2u}{{d}x^2} = -q_x$ en torsie $ GI \\cfrac{{d}^2\\varphi_x}{{d}x^2} = -q_T$ hebben we in dit geval niet nodig.\n", + "\n", + "**Element AC**\n", + "\n", + "Omdat dit element onbelast is, geeft de belastingsvergelijking voor dit element:\n", + "\n", + "$$q_{z,{\\rm{AC}}}=0$$\n", + "\n", + "wat leidt tot de vergelijkingen:\n", + "\n", + "$$ V_{\\rm{AC}} \\left(x\\right) = C_1$$\n", + "\n", + "$$ M_{\\rm{AC}} \\left(x\\right) = C_1 \\cdot x + C_2 $$\n", + "\n", + "$$ \\kappa_{\\rm{AC}} \\left(x\\right) = \\cfrac{C_1 \\cdot x + C_2}{5000}$$\n", + "\n", + "$$ \\varphi_{\\rm{AC}} \\left(x\\right) = \\cfrac{\\cfrac{1}{2}\\cdot C_1 \\cdot x^2 + C_2 \\cdot x}{5000} + C_3$$\n", + "\n", + "$$ w_{\\rm{AC}} \\left(x\\right) = \\cfrac{-\\cfrac{1}{6}\\cdot C_1 \\cdot x^3 - \\cfrac{1}{2} C_2 \\cdot x^2}{5000} - C_3 \\cdot x + C_4$$\n", + "\n", + "Vanwege de oplegging bij A kan een randvoorwaarde worden geformuleerd als:\n", + "\n", + "```{math}\n", + ":label: BC_1\n", + "w_{\\rm{AC}} \\left(0\\right) = 0\n", + "```\n", + "\n", + "Omdat A ook een onbelast uiteinde is, kan een andere randvoorwaarde worden geformuleerd als:\n", + "\n", + "```{math}\n", + ":label: BC_2\n", + "M_{\\rm{AC}} \\left(0\\right) = 0\n", + "```\n", + "\n", + "Als dit uiteinde belast werd kon met een krachten- of momentenevenwicht ook een relatie gevonden worden.\n", + "\n", + "**Element BC**\n", + "\n", + "Omdat dit element belast is, geeft de belastingsvergelijking voor dit element:\n", + "\n", + "$$q_{z,\\rm{BC}}=10$$\n", + "\n", + "wat leidt tot de vergelijkingen:\n", + "\n", + "$$ V_{\\rm{BC}} \\left(x\\right) = -10 \\cdot x + C_5$$\n", + "\n", + "$$ M_{\\rm{BC}} \\left(x\\right) = -5 \\cdot x^2 + C_5 \\cdot x + C_6 $$\n", + "\n", + "$$ \\kappa_{\\rm{BC}} \\left(x\\right) = \\cfrac{-5 \\cdot x^2 + C_5 \\cdot x + C_6}{5000}$$\n", + "\n", + "$$ \\varphi_{\\rm{BC}} \\left(x\\right) = \\cfrac{-\\cfrac{5}{3} \\cdot x^3 + \\cfrac{1}{2}\\cdot C_5 \\cdot x^2 + C_6 \\cdot x}{5000} + C_7$$\n", + "\n", + "$$ w_{\\rm{BC}} \\left(x\\right) = \\cfrac{\\cfrac{5}{12} \\cdot x^4 -\\cfrac{1}{6}\\cdot C_5 \\cdot x^3 - \\cfrac{1}{2} C_6 \\cdot x^2}{5000} - C_7 \\cdot x + C_8$$\n", + "\n", + "Vanwege de inklemming bij B kunnen twee randvoorwaarden direct worden geformuleerd:\n", + "\n", + "```{math}\n", + ":label: BC_3\n", + "\\varphi_{\\rm{BC}} \\left(4\\right) = 0\n", + "```\n", + "\n", + "```{math}\n", + ":label: BC_4\n", + "w_{\\rm{BC}} \\left(4\\right) = 0\n", + "```\n", + "\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_4\n", + "\n", + "Bij C kan een randvoorwaarde worden geïdentificeerd uit de consistentie van verplaatsingen: zowel de neerwaartse als rotatieverplaatsing moeten hetzelfde zijn als de neerwaartse en rotatieverplaatsing aan de linkerkant van element BE:\n", + "\n", + "```{math}\n", + ":label: BC_5\n", + "w_{\\rm{AC}} \\left(4\\right) = w_{\\rm{BC}} \\left(0\\right)\n", + "```\n", + "\n", + "```{math}\n", + ":label: BC_6\n", + "\\varphi_{\\rm{AC}} \\left(4\\right) = \\varphi_{\\rm{BC}} \\left(0 \\right)\n", + "```\n", + "\n", + "Ten slotte moet er ook consistentie zijn met de snedekrachten, zoals getoond in het vrijlichaamdiagram:\n", + "\n", + "```{figure} intro_data/FBD_C.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram knoop C\n", + "```\n", + "\n", + "Verticaal evenwicht geeft:\n", + "\n", + "```{math}\n", + ":label: BC_7\n", + "\\sum {{{\\left. {{F_{\\rm{v}}}} \\right|}^{\\rm{C}}} = 0} \\to -{V_{\\rm{AC}}}\\left(4\\right) + V_{\\rm{BC}}\\left(0\\right) = 0\n", + "```\n", + "\n", + "Momentevenwicht geeft:\n", + "\n", + "```{math}\n", + ":label: BC_8\n", + "\\sum {{{\\left. {{T}} \\right|}_{\\rm{C}}^{\\rm{C}}} = 0} \\to -{M_{{\\rm{AC}}}}\\left(4\\right) + M_{\\rm{BC}}\\left(0\\right) = 0\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", + "\n", + "\n", + "::::::{codex-start}\n", + ":nonumber: true\n", + ":label: df_example_5\n", + "\n", + "::::::\n", + "\n", + "Er zijn 8 randvoorwaarden geformuleerd met 8 integratieconstanten. Deze kunnen nu worden opgelost. \n", + "\n", + "Het invullen van de randvoorwaarden geeft:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "C_{4} = 0 \\\\\n", + "C_{2} = 0 \\\\\n", + "- \\cfrac{4 C_{5}}{1875} - \\cfrac{C_{6}}{625} - 4 C_{7} + C_{8} + \\cfrac{8}{375} = 0 \\\\\n", + "\\cfrac{C_{5}}{625} + \\cfrac{C_{6}}{1250} + C_{7} - \\cfrac{8}{375} = 0 \\\\\n", + "- \\cfrac{4 C_{1}}{1875} - \\cfrac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0 \\\\\n", + "\\cfrac{C_{1}}{625} + \\cfrac{C_{2}}{1250} + C_{3} - C_{7} = 0 \\\\\n", + "4 C_{1} + C_{2} - C_{6} = 0 \\\\\n", + "C_{1} - C_{5} = 0 \\\\\n", + "\\end{align}\n", + "$$\n", + "\n", + "Het oplossen hiervan geeft:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "C_{1} = \\cfrac{35}{8} = 4.375 \\\\\n", + "C_{2} = 0 \\\\\n", + "C_{3} = - \\cfrac{1}{150} \\approx -0.006667 \\\\\n", + "C_{4} = 0 \\\\\n", + "C_{5} = \\cfrac{35}{8} = 4.375\\\\\n", + "C_{6} = \\cfrac{35}{2} = 17.5 \\\\\n", + "C_{7} = \\cfrac{1}{3000} \\approx 0.0003333\\\\\n", + "C_{8} = \\cfrac{13}{750} \\approx 0.01733\n", + "\\end{align}\n", + "$$\n", + "\n", + "Nu is de volledige krachtenverdeling en verplaatsingen van de constructie opgelost. Bijvoorbeeld de momentenverdeling in BC die kan worden uitgezet als:\n", + "\n", + "```{figure} intro_data/Momentenlijn.svg\n", + ":align: center\n", + "\n", + "Momentenlijn BC\n", + "```\n", + "\n", + "De krachtsverdeling en verplaatsingen kunnen no ook \n", + "\n", + "**Oplossen met digitale hulpmiddelen**\n", + "Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python.\n", + "\n", + "\n", + "$$\\left[\\begin{array}{cccccccc}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{array}\\right] \\left[ \\begin{array}{cccccccc} C_1\\\\C_2\\\\C_3\\\\C_4\\\\C_5\\\\C_6\\\\C_7\\\\C_8 \\end{array} \\right] = \\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]$$\n", + "\n", + "[Hier](rref_ti_84) is uitgelegd hoe je dit op een grafische rekenmachine kan oplossen.\n", + "\n", + "Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt.\n", + "\n", + "Beide opties zijn hieronder getoond" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAs9JREFUeJzl102o1FUYx/HPaEh2q3sxCgKtqNRdS7tgQi8g0SZXLgLBCiEsYnITFPTwBLqMUBRq0S2iVhEGtRF6WVTULktLbwTtgsDK6nbpzXFxztTcaeY2M3eGS/SF4YHznJfn9z/nPOeZRqvV0ovM3IwHsBPXYQY/4CRex4sR8UvPwatEZm7E07gbV+EbHEdGxPftfo1u0ZnZQOJxrMOH+BTncb3yETbg/YjYMWkhg5KZNymxXoM3cAbbcAfOYntEnINLeox/AXvxOe6LiJNdk6/HAdw8ofhH5Zgi+NGIONJuzMxn8BgO4iFY0zkqMw8ogr/AbLdgiIjFiDiI/ZOKflgy80blBH6No13uwAL2ZOYUHaIz81rla/yB3RHx03ILRcTi+MJeMXdWeyIiLnQ6qo4PcBlmWXq8m7gUcxFxatJRZmZTSY6D8klEHO/j21rtfB//l8pJ2IK3O0XvqvblIQJZCU0lMQ7KS0om7sV0tef7+NvtM9SdzszLla/QwsdDBPIXmfmEcj2ORsQj/9Y/Im4YZZ0RaVTb4u87fXW1P47y9mbmLPYpT9tq0N7J6T7+Kzv7tY/3QrXrM3NtRPw56GqZOY1X8CCeGmJc0/ju9Nlqt/Txb652nrrTEfGtku7X4fblVs7MNV1Nz+O1iHhnuXE9aCrPyaC/XcvM9W61O7vjy8wrsB2L+Iil2fvZ+juWmfdGxJmuwQ2lvLsfu2vbPqVI2TOw1Mo473REfJWZJ5QM/TCOdLgTU3guIhZYKvowblHq7c/qJPP4HZtwGzbiVcjMrTiEHRHx27gErID9Shl6ODPvUgqsW5UydB5Ptjv2qr3vUZLSNiXB/awU7qfxFt6MiHOZuRdz6Lz/a5UMeQFTEfHrBMT1JTM36f+H47t2v3+IHmKBGWXnO5lTCoFDOB0Ro00+YUYW3YvMfA+nBnmnV5PuTPy/YKw7/V/hImUh8qiSnFTZAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle C_{4} = 0$" + ], + "text/plain": [ + "C₄ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAt9JREFUeJzV10+IVXUUB/DPTBGZmWIYBFpSaruWJZmRBSZtkhYGkZBBUAoxSRDS4nACV1HISANt+kPQMoxqIw1tKmqn/TGdCNwFQX+smYbIei1+99ad17vje773kL5wObx7fud3zvf3O+e8cyc6nY5eyMzNeAw7cQPW4GecxNt4PSJ+62l8iZCZ6/E8duFafIdjyIj4qV430U06MyeQeBZX4BN8jnO4UTmEtfgoIraPm0i/yMyblVivwzs4jduwA2ewLSJ+gMt72L+KR3EKD0fEya7NV+AgNo0p/ovFjEL4qYg4Wr/MzJfwNA7jCZhsWmXmQYXw19jaTRgiYjEiDmP/uKIfFJl5k5KBZ/FylzqwgL2ZuZIG6cy8XjmN89gTEb8u5ygiFkcX9tC4p5LHI+KvpqLi8TGuwlaWpvcUrsRrEfHluKPMzCmlOfaLExFxrEV3SyXnWvTfKJmwBbNN0rsr+eYAgQyDKaUx9os3lE7cC6srea5FX79fQ3XTmXm1cgodfNZvFJl5CA8qJ/07PsWhfjIlIjb262cEmKhkh39rel0lfxnwv/dupWveodTVeXyQmWuHj3Mg1De5ukV/TXNdnd4LlVyRmZdFxJ/9eIqI+5q/M3NvtfE2vLuc7Yhr+kwlt7ToN1dyjop0RHyfmWexUbm92TbPmTnZ3SEbWKVkz49t9g1MGV1Nf1jJnd3xZeYq5RIWlfJb0r2PVM9MZj4QEaebu1aT2i7sw54W50dwot58OYyypiPi28w8rnToAzjaUCdW4pWIWGAp6WncqszbX1SbzOEPbMCdWI+3ejnOzBdwF7b3Wx4jxn5lDJ3OzHuVAet2ZQydw3P1wl6z9/14XJlb12FeGdy/wvt4r55hGzYv4hHsiIhT4+F0YWTmBu0fHP+U3H9IX4SjaTzkEhMeBEORzswZ5YZ3Kx8oNeYjYn640MaHyQsvWRZPKh17Vkml+nlmyH3HiqHT+/+IvwHtQ/pzJbnwjAAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle C_{2} = 0$" + ], + "text/plain": [ + "C₂ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle - \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0$" + ], + "text/plain": [ + " 4⋅C₅ C₆ 8 \n", + "- ──── - ─── - 4⋅C₇ + C₈ + ─── = 0\n", + " 1875 625 375 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0$" + ], + "text/plain": [ + "C₅ C₆ 8 \n", + "─── + ──── + C₇ - ─── = 0\n", + "625 1250 375 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle - \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0$" + ], + "text/plain": [ + " 4⋅C₁ C₂ \n", + "- ──── - ─── - 4⋅C₃ + C₄ - C₈ = 0\n", + " 1875 625 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0$" + ], + "text/plain": [ + "C₁ C₂ \n", + "─── + ──── + C₃ - C₇ = 0\n", + "625 1250 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAUCAYAAAAdtqaHAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAABSNJREFUeJztmmuoVUUYhp9jIpmaQhQIWqFp5A8pMjO8kB5QqR8eioxCRbtAqdjJbmjY62cYgVhHJSG01CQhsDAKBFG0sDQkVDKzk5L4I8HQsjSVLrsf36zarc5ae+3bWUfYDyxm77m+s5j5Zuab1VQoFEjCzKYD74S/j0tam5J3CPAIMBG4HugH/AwcBD4A1kv6LbGxnGnozwczGwAsASYD1wAngS2ASfopytc9pYKBwCrgHNA7JV8TYMALQA/gc2AzcBa4AX9xzcBDwNgq+lQXGvrzw8wG43qvAz4EjgAjgaeAyWY2WtJpSBioofPrgNP4bHw2pb23gZnAYeBhSQdjdfUE5gM3Vd6lZMxsZtA6XtKuCqrIVX8NuJz1r8YH6TxJq6JIM3sNeBpYCjwB0C2hgnnABGAWcD6pFTObj7+kb4BR8ZcEIOmCpKXA7Ep6Uk8a+vPDzAbh1v448EYsWfi4m25mvaCDgWpmtwCvAiskfZrSUH98xP8BTJX0a5owSReyd6P+NPTnzoQQbpP0V3FC6MtnwFXAKIgt/WbWHdgInAAWlmioFbgSWCfpUNWyO59WGvozY2at+AEtKwckbUlJvzmE7Qnp3+EWdyiwI75HfQm4DRiTYQa2hHBjiXxdlZYQNvRnoxU/nGVlA356T6JvCM8mpEfx/aDIoprZSNyKLpe0J02BmfXGR3oB+KKU4ljZccBzwO1Af+ABSZszlj1O8svaaWbxuA2SZnZQTzX6FwD34RbhErAXWJDFqpXQ3xHvSprWQT0V6w/l++Pbu3twj85RYK6kT5LKSLqx3HaqpCmEBQgDtWjJbwcWZajk2hD+UoFvrhfu21sHvF9m2Tb+v/zcCkzBZ/DxWNqBhHqq0X83flrdh7/MJcB2Mxsm6UyJsseAi2W09UNCfMX6zawfvv/bDdwL/AgMAk6VU08NiCxm34T0q4vzRRY1mqEAFzuwTABrzGwNsAJ4JcT1NLMrJP2ZVZ2krcBWgIR20sq2xeOCe2oK7tDelbGqyJNRif5Jsfan4y9zNPBRibLNWdspQcX6geeBk5JmFMV9X6pQHfao34ZwaEL6kBC2w7+n/kvAWwnP/pBnd/i/R9Ip3Hr1wC1MImaW5ALLjRrr74O/x1LWtGZUqb8F2Gtmm8zslJkdMLO5wXeeRivuNsr6tJSob2cIJ8Y1mlkffOJfwLdWblHDwemxhI4uxg9YG2JXqG3hWW1mUyQdiZVrwq/FZgFTS4jOgzZqo78N32LsrYfIEu22Ub7+QcAc4HVgEr51WoXvBeP+zH+o9R5V0jEz24af7OcEDRGGbxHflHQeUq5QM7ASGI7fL38VGm0HfgcGAmOAAcCmKtqoJ1XrN7NlwDhgbJnLby2oVH834EtJC8L//WY2FB8siQO1TszGr1BXmlkzfnFxJzAe78uLUcaKl2VJBUmP4hvyj/GZOQe3zMOBPfitybxK26gn1eo3s+XADKBZ0tFOkPwfqtB/Evg6FneY8rwRNUHSMWAEsB4foM8Ag/FJeFd0zw/QlPb1VL0xswJluKe6Cma2EngQ/77gcN56ysHMNgEDJY0tinsZuF/SsPyUpdPpAzX4AKMPJPbjPtXtwBlJJzpVTAWY2WpgGn5YKB6k5ySdy0VUGZjZHfhyuxh4Dz9/rAUWSurspT8zeZzIR+ADNPImLAu/l+SgpRKexE/6O/BlNHrSvjDrMkjah0+yqcAh/HuBRbhvuMuS69LfoEFWupyPs0GDjmgM1AaXBX8Db+ZLKnj2sa4AAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle 4 C_{1} + C_{2} - C_{6} = 0$" + ], + "text/plain": [ + "4⋅C₁ + C₂ - C₆ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAUCAYAAABs4ghcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA6lJREFUeJztmV2IVGUYx3+zimhWCn0KWy2adhGEkNSGFdWCCF20NwlFQqUXYmCTEhEh//6CV4aIktFNukR5Y2EUCEJ4VZt3iWF+sPkBfZOlohukTRfve7az48x4zsyZ2R3YHxwezvv5f+Y57+eUKpUKtbC9EHgZWAbcDcwF/gIOA58CuyVdrll5gulW7bZ7gU3AcuAW4GdgH2BJf6bLTq9RuQQYeAOYAXwN7AXOA/cQfowB4DngsXY50Qxdrn0BQe/twGfAMeAh4FVgue2lkv5Iyl8TOOAD4EXgKPC8pMNVHcwC1gP3tsOBFulm7TsJQVsnaUeSaHsr8BqwGViTpPeka9peT3D8e6C/2nEASaOSNgNr26G+Wbpc+3zCbHAaeLcqW8AlYKXt2UliT6ryPEJUrwArJF1s1Jmk0WJkt043a488Fe0BSf+mM6IvXwE3AP1JenqqLAMzgV2SvmuvzsIp02HttsuETU9WvpW0r07efdGeqJN/kjAiFwFfwvjADUb7YQ4xk4XBaDupvUzY8GRliLBDrMWcaM/XyU/S5yYJ0wFs30iIZgU4lFWJ7ceB14EHgXnAs5L2Zqx7mnyOfyTphRrtNKU91n2bsIak+VXSnderK6kvT18tUop27OyWjLjbor2Q83wzm3A22gV8klPMCPB3jvI/1UlvVnvCceCJ1PvVJtpolWREzamTf3NVubHAXYp2lu1pkjKJl7Qf2A9gO5dSSQO5KtSnKe0prkj6JW+nBa9xx6NdVCd/YbRja2APgKTfCFvRGYz/+q7Bdk+j/E5TgPb5tn+0fcr2Htt9GbsuE6bZrM9gg7YORrusWqPtm4ClwCjwTZKe3pxsi89O289IOlbVQIlwFfMSsCKjc51iG81pPwSsJHzJdwBvAsO275d0rlGHRa5xkkZsHyDsHF8BdqSyTViS3peUzC7jArcdeIBwx3ckNnQC+Ae4C3gU6AU+LkpwgTSlPU71CUdsDwM/EA7yW9svexxrCVde220PEC4SHgaeJPjyVrrw2LCUVJG0Cnga+AJYTIj+asKPMkxwaF27PchLUdrjF32U/9eUjiFpBFgC7CYEbAOwgPBRPpK+pwQo1ft3IC+2K+Q4DkxGbM8ETgHvSdo00XoaUeuSOTPxDJW+sO2zvRg4J+lsK213AtvvAJ8DZwkXvBsJ68nQROrKQkuBIwztg6n3LdEOEaamyU4vsAe4FfidsGvrl3RmQlVloLCpcorOMqnOZFNkZypwXcp/TvVqwtp6oi4AAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle C_{1} - C_{5} = 0$" + ], + "text/plain": [ + "C₁ - C₅ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C_{4} = 0\n", + "C_{2} = 0\n", + "- \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0\n", + "\\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0\n", + "- \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0\n", + "\\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0\n", + "4 C_{1} + C_{2} - C_{6} = 0\n", + "C_{1} - C_{5} = 0\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "%config InlineBackend.figure_formats = ['svg']\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = 10 \n", + "EI = 5000 \n", + "EA = 20000 \n", + "F = 150\n", + "\n", + "q_AC = 0\n", + "q_BC = q\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", + "\n", + "V_AC = -sym.integrate(q_AC, x) + C_1\n", + "M_AC = sym.integrate(V_AC, x) + C_2\n", + "kappa_AC = M_AC/EI\n", + "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", + "w_AC = -sym.integrate(phi_AC, x) + C_4\n", + "\n", + "V_BC = -sym.integrate(q_BC, x) + C_5\n", + "M_BC = sym.integrate(V_BC, x) + C_6\n", + "kappa_BC = M_BC/EI\n", + "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", + "w_BC = -sym.integrate(phi_BC, x) + C_8\n", + "\n", + "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", + "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", + "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", + "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", + "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", + "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", + "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", + "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", + "\n", + "display(eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8)\n", + "for eq in [eq1, eq2, eq3, eq4, eq5, eq6, eq7, eq8]:\n", + " print(sym.latex(eq))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\left\\{ C_{1} : \\frac{35}{8}, \\ C_{2} : 0, \\ C_{3} : - \\frac{1}{150}, \\ C_{4} : 0, \\ C_{5} : \\frac{35}{8}, \\ C_{6} : \\frac{35}{2}, \\ C_{7} : \\frac{1}{3000}, \\ C_{8} : \\frac{13}{750}\\right\\}$" + ], + "text/plain": [ + "⎧ 13 ⎫\n", + "⎨C₁: 35/8, C₂: 0, C₃: -1/150, C₄: 0, C₅: 35/8, C₆: 35/2, C₇: 1/3000, C₈: ───⎬\n", + "⎩ 750⎭" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'\\\\left\\\\{ C_{1} : \\\\frac{35}{8}, \\\\ C_{2} : 0, \\\\ C_{3} : - \\\\frac{1}{150}, \\\\ C_{4} : 0, \\\\ C_{5} : \\\\frac{35}{8}, \\\\ C_{6} : \\\\frac{35}{2}, \\\\ C_{7} : \\\\frac{1}{3000}, \\\\ C_{8} : \\\\frac{13}{750}\\\\right\\\\}'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "display(sol)\n", + "sym.latex(sol)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T16:58:07.214407\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.3, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "M_numpy = sym.lambdify(x, M_BC.subs(sol))\n", + "x_numpy = np.linspace(0,4,100)\n", + "plt.figure()\n", + "plt.plot(x_numpy,M_numpy(x_numpy))\n", + "plt.xlabel('$x$')\n", + "plt.ylabel('$M_{BC}$');\n", + "ax = plt.gca()\n", + "ax.spines['right'].set_color('none')\n", + "ax.spines['top'].set_color('none')\n", + "ax.spines['bottom'].set_position('zero')\n", + "ax.spines['left'].set_position('zero')\n", + "ax.invert_yaxis()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\left[\\begin{matrix}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{matrix}\\right]\n", + "\\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]\n" + ] + } + ], + "source": [ + "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8), (C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "print(sym.latex(A))\n", + "print(sym.latex(b))\n", + "A = np.array(A).astype(np.float64)\n", + "b = np.array(b).astype(np.float64)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "A = np.array([\n", + " [0, 0, 0, 1, 0, 0, 0, 0],\n", + " [0, 1, 0, 0, 0, 0, 0, 0],\n", + " [0, 0, 0, 0, -4/1875, -1/625, -4, 1],\n", + " [0, 0, 0, 0, 1/625, 1/1250, 1, 0],\n", + " [-4/1875, -1/625, -4, 1, 0, 0, 0, -1],\n", + " [1/625, 1/1250, 1, 0, 0, 0, -1, 0],\n", + " [4, 1, 0, 0, 0, -1, 0, 0],\n", + " [1, 0, 0, 0, -1, 0, 0, 0]\n", + "])\n", + "\n", + "b = np.array([\n", + " 0,\n", + " 0,\n", + " -8/375,\n", + " 8/375,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 4.37500000e+00],\n", + " [ 0.00000000e+00],\n", + " [-6.66666667e-03],\n", + " [ 0.00000000e+00],\n", + " [ 4.37500000e+00],\n", + " [ 1.75000000e+01],\n", + " [ 3.33333333e-04],\n", + " [ 1.73333333e-02]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.linalg.solve(A,b)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]$" + ], + "text/plain": [ + "Matrix([\n", + "[ 4.375],\n", + "[ 0],\n", + "[-0.00666666666666667],\n", + "[ 0],\n", + "[ 4.375],\n", + "[ 17.5],\n", + "[0.000333333333333334],\n", + "[ 0.0173333333333333]])" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]\n", + "\\left[\\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 4.375\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -0.00666666666666667\\\\0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 4.375\\\\0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 17.5\\\\0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0.000333333333333334\\\\0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0.0173333333333333\\end{matrix}\\right]\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "\n", + "A_sym = sym.Matrix(A)\n", + "b_sym = sym.Matrix(b)\n", + "\n", + "sol_sym = A_sym.LUsolve(b_sym)\n", + "\n", + "display(sol_sym)\n", + "\n", + "print(sym.latex(sol_sym))\n", + "\n", + "identity = sym.eye(sol_sym.rows)\n", + "rr_augmented = identity.row_join(sol_sym)\n", + "print(sym.latex(rr_augmented))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C_1 = 35/8 ≈ 4.37500000000000\n", + "C_2 = 0 ≈ 0\n", + "C_3 = -1/150 ≈ -0.00666666666666667\n", + "C_4 = 0 ≈ 0\n", + "C_5 = 35/8 ≈ 4.37500000000000\n", + "C_6 = 35/2 ≈ 17.5000000000000\n", + "C_7 = 1/3000 ≈ 0.000333333333333333\n", + "C_8 = 13/750 ≈ 0.0173333333333333\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = 10 \n", + "EI = 5000 \n", + "EA = 20000 \n", + "F = 150\n", + "\n", + "q_AC = 0\n", + "q_BC = q\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", + "\n", + "V_AC = -sym.integrate(q_AC, x) + C_1\n", + "M_AC = sym.integrate(V_AC, x) + C_2\n", + "kappa_AC = M_AC/EI\n", + "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", + "w_AC = -sym.integrate(phi_AC, x) + C_4\n", + "\n", + "V_BC = -sym.integrate(q_BC, x) + C_5\n", + "M_BC = sym.integrate(V_BC, x) + C_6\n", + "kappa_BC = M_BC/EI\n", + "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", + "w_BC = -sym.integrate(phi_BC, x) + C_8\n", + "\n", + "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", + "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", + "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", + "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", + "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", + "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", + "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", + "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", + "\n", + "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "for k, v in sol.items():\n", + " print(f\"{k} = {v} ≈ {v.evalf()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "::::::{codex-end}\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Meer voorbeelden\n", + "Het voorbeeld in hoofdstuk 2.7.3 van het boek Mechanica, spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013` richt zich op extensie. Voorbeeld 3b in hoofdstuk 8.2 is een buigingsprobleem.\n", + "\n", + "## Instructies in collegevorm\n", + "\n", + "Dit onderwerp is [in les 3](https://collegerama.tudelft.nl/Mediasite/Channel/public-ceg-ctb2210/watch/757766716fcb45718fca678dadaa396d1d?sortBy=most-recent) gepresenteerd in collegevorm van 0:40:00 tot 0:48:30\n", + "\n", + "## Opgaves\n", + "\n", + "- Opgaves 2.48 en 2.50 in hoofdstuk 2.9 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter2/).\n", + "- Opgaves 8.7, 8.10, 8.11 in hoofdstuk 8.6 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter8/)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/instructie_2.ipynb b/book/instructie_2.ipynb new file mode 100644 index 0000000..4b1a93d --- /dev/null +++ b/book/instructie_2.ipynb @@ -0,0 +1,1470 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "````{margin}\n", + "```{attributiongrey} Attribution\n", + ":class: attribution\n", + "\n", + "Deze pagina is aangepast van https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro.ipynb\n", + "\n", + "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro_data/Tekening1.vsdx\n", + "\n", + "```\n", + "```` " + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Instructie_new\n", + "\n", + "In constructiemechanica 2 heb je geleerd hoe je met differentiaalvergelijkingen voor statisch bepaalde constructies de krachtsverdeling en vervormingen kan vinden. Precies dezelfde aanpak werkt ook voor statisch onbepaalde constructie, met als enige verschil dat we de inwendige krachten niet kunnen vinden zonder ook de vergelijkingen en randvoorwaarden voor verplaatsingen op te stellen. Dat doen we volgens de volgende stappen:\n", + "\n", + "::::::{prf:algorithm} Differentiaalvergelijkingen toepassen\n", + ":nonumber: true\n", + ":label: differentiaalvergelijkingen_algoritme\n", + "\n", + "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", + "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", + "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", + "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", + "\n", + "::::::\n", + "\n", + "Als voorbeeld bepalen we de een simpele statisch onbepaalde constructie:\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_0\n", + "\n", + "Gegeven een constructie zoals hieronder weergegeven:\n", + "\n", + "```{figure} intro_data/structure.svg\n", + "---\n", + "align: center\n", + "---\n", + "Constructie, $EI = 5000 \\ \\rm{kNm^2}$\n", + "```\n", + "::::::\n", + "\n", + "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_1\n", + "\n", + "In dit voorbeeld moeten we element AB in twee delen opsplitsen omdat de verdeelde belasting een discontinuïteit veroorzaakt.\n", + "\n", + "Voor AC leidt dit tot:\n", + "\n", + "```{figure} intro_data/FBD_AC.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram element AC\n", + "```\n", + "\n", + "Voor BC leidt dit tot:\n", + "\n", + "```{figure} intro_data/FBD_BC.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram element BC\n", + "```\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_3\n", + "\n", + "In dit voorbeeld is er enkel invloed door buiging, dus hebben we aan deze differentiaalvergelijking genoeg: $ -EI \\cfrac{{d}^4w}{{d}x^4} = -q_z$. De differentiaalvergelijkingen voor rek $ EA \\cfrac{{d}^2u}{{d}x^2} = -q_x$ en torsie $ GI \\cfrac{{d}^2\\varphi_x}{{d}x^2} = -q_T$ hebben we in dit geval niet nodig.\n", + "\n", + "**Element AC**\n", + "\n", + "Omdat dit element onbelast is, geeft de belastingsvergelijking voor dit element:\n", + "\n", + "$$q_{z,{\\rm{AC}}}=0$$\n", + "\n", + "wat leidt tot de vergelijkingen:\n", + "\n", + "$$ V_{\\rm{AC}} \\left(x\\right) = C_1$$\n", + "\n", + "$$ M_{\\rm{AC}} \\left(x\\right) = C_1 \\cdot x + C_2 $$\n", + "\n", + "$$ \\kappa_{\\rm{AC}} \\left(x\\right) = \\cfrac{C_1 \\cdot x + C_2}{5000}$$\n", + "\n", + "$$ \\varphi_{\\rm{AC}} \\left(x\\right) = \\cfrac{\\cfrac{1}{2}\\cdot C_1 \\cdot x^2 + C_2 \\cdot x}{5000} + C_3$$\n", + "\n", + "$$ w_{\\rm{AC}} \\left(x\\right) = \\cfrac{-\\cfrac{1}{6}\\cdot C_1 \\cdot x^3 - \\cfrac{1}{2} C_2 \\cdot x^2}{5000} - C_3 \\cdot x + C_4$$\n", + "\n", + "Vanwege de oplegging bij A kan een randvoorwaarde worden geformuleerd als:\n", + "\n", + "```{math}\n", + ":label: BC_1\n", + "w_{\\rm{AC}} \\left(0\\right) = 0\n", + "```\n", + "\n", + "Omdat A ook een onbelast uiteinde is, kan een andere randvoorwaarde worden geformuleerd als:\n", + "\n", + "```{math}\n", + ":label: BC_2\n", + "M_{\\rm{AC}} \\left(0\\right) = 0\n", + "```\n", + "\n", + "Als dit uiteinde belast werd kon met een krachten- of momentenevenwicht ook een relatie gevonden worden.\n", + "\n", + "**Element BC**\n", + "\n", + "Omdat dit element belast is, geeft de belastingsvergelijking voor dit element:\n", + "\n", + "$$q_{z,\\rm{BC}}=10$$\n", + "\n", + "wat leidt tot de vergelijkingen:\n", + "\n", + "$$ V_{\\rm{BC}} \\left(x\\right) = -10 \\cdot x + C_5$$\n", + "\n", + "$$ M_{\\rm{BC}} \\left(x\\right) = -5 \\cdot x^2 + C_5 \\cdot x + C_6 $$\n", + "\n", + "$$ \\kappa_{\\rm{BC}} \\left(x\\right) = \\cfrac{-5 \\cdot x^2 + C_5 \\cdot x + C_6}{5000}$$\n", + "\n", + "$$ \\varphi_{\\rm{BC}} \\left(x\\right) = \\cfrac{-\\cfrac{5}{3} \\cdot x^3 + \\cfrac{1}{2}\\cdot C_5 \\cdot x^2 + C_6 \\cdot x}{5000} + C_7$$\n", + "\n", + "$$ w_{\\rm{BC}} \\left(x\\right) = \\cfrac{\\cfrac{5}{12} \\cdot x^4 -\\cfrac{1}{6}\\cdot C_5 \\cdot x^3 - \\cfrac{1}{2} C_6 \\cdot x^2}{5000} - C_7 \\cdot x + C_8$$\n", + "\n", + "Vanwege de inklemming bij B kunnen twee randvoorwaarden direct worden geformuleerd:\n", + "\n", + "```{math}\n", + ":label: BC_3\n", + "\\varphi_{\\rm{BC}} \\left(4\\right) = 0\n", + "```\n", + "\n", + "```{math}\n", + ":label: BC_4\n", + "w_{\\rm{BC}} \\left(4\\right) = 0\n", + "```\n", + "\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", + "\n", + "::::::{prf:example}\n", + ":nonumber: true\n", + ":label: df_example_4\n", + "\n", + "Bij C kan een randvoorwaarde worden geïdentificeerd uit de consistentie van verplaatsingen: zowel de neerwaartse als rotatieverplaatsing moeten hetzelfde zijn als de neerwaartse en rotatieverplaatsing aan de linkerkant van element BE:\n", + "\n", + "```{math}\n", + ":label: BC_5\n", + "w_{\\rm{AC}} \\left(4\\right) = w_{\\rm{BC}} \\left(0\\right)\n", + "```\n", + "\n", + "```{math}\n", + ":label: BC_6\n", + "\\varphi_{\\rm{AC}} \\left(4\\right) = \\varphi_{\\rm{BC}} \\left(0 \\right)\n", + "```\n", + "\n", + "Ten slotte moet er ook consistentie zijn met de snedekrachten, zoals getoond in het vrijlichaamdiagram:\n", + "\n", + "```{figure} intro_data/FBD_C.svg\n", + ":align: center\n", + "\n", + "Vrijlichaamdiagram knoop C\n", + "```\n", + "\n", + "Verticaal evenwicht geeft:\n", + "\n", + "```{math}\n", + ":label: BC_7\n", + "\\sum {{{\\left. {{F_{\\rm{v}}}} \\right|}^{\\rm{C}}} = 0} \\to -{V_{\\rm{AC}}}\\left(4\\right) + V_{\\rm{BC}}\\left(0\\right) = 0\n", + "```\n", + "\n", + "Momentevenwicht geeft:\n", + "\n", + "```{math}\n", + ":label: BC_8\n", + "\\sum {{{\\left. {{T}} \\right|}_{\\rm{C}}^{\\rm{C}}} = 0} \\to -{M_{{\\rm{AC}}}}\\left(4\\right) + M_{\\rm{BC}}\\left(0\\right) = 0\n", + "```" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", + "\n", + "\n", + "::::::{prf:example-start}\n", + ":nonumber: true\n", + ":label: df_example_5\n", + "\n", + "::::::\n", + "\n", + "Er zijn 8 randvoorwaarden geformuleerd met 8 integratieconstanten. Deze kunnen nu worden opgelost. \n", + "\n", + "Het invullen van de randvoorwaarden geeft:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "C_{4} = 0 \\\\\n", + "C_{2} = 0 \\\\\n", + "- \\cfrac{4 C_{5}}{1875} - \\cfrac{C_{6}}{625} - 4 C_{7} + C_{8} + \\cfrac{8}{375} = 0 \\\\\n", + "\\cfrac{C_{5}}{625} + \\cfrac{C_{6}}{1250} + C_{7} - \\cfrac{8}{375} = 0 \\\\\n", + "- \\cfrac{4 C_{1}}{1875} - \\cfrac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0 \\\\\n", + "\\cfrac{C_{1}}{625} + \\cfrac{C_{2}}{1250} + C_{3} - C_{7} = 0 \\\\\n", + "4 C_{1} + C_{2} - C_{6} = 0 \\\\\n", + "C_{1} - C_{5} = 0 \\\\\n", + "\\end{align}\n", + "$$\n", + "\n", + "Het oplossen hiervan geeft:\n", + "\n", + "$$\n", + "\\begin{align}\n", + "C_{1} = \\cfrac{35}{8} = 4.375 \\\\\n", + "C_{2} = 0 \\\\\n", + "C_{3} = - \\cfrac{1}{150} \\approx -0.006667 \\\\\n", + "C_{4} = 0 \\\\\n", + "C_{5} = \\cfrac{35}{8} = 4.375\\\\\n", + "C_{6} = \\cfrac{35}{2} = 17.5 \\\\\n", + "C_{7} = \\cfrac{1}{3000} \\approx 0.0003333\\\\\n", + "C_{8} = \\cfrac{13}{750} \\approx 0.01733\n", + "\\end{align}\n", + "$$\n", + "\n", + "Nu is de volledige krachtenverdeling en verplaatsingen van de constructie opgelost. Bijvoorbeeld de momentenverdeling in BC die kan worden uitgezet als:\n", + "\n", + "```{figure} intro_data/Momentenlijn.svg\n", + ":align: center\n", + "\n", + "Momentenlijn BC\n", + "```\n", + "\n", + "De krachtsverdeling en verplaatsingen kunnen no ook \n", + "\n", + "**Oplossen met digitale hulpmiddelen**\n", + "Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python.\n", + "\n", + "\n", + "$$\\left[\\begin{array}{cccccccc}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{array}\\right] \\left[ \\begin{array}{cccccccc} C_1\\\\C_2\\\\C_3\\\\C_4\\\\C_5\\\\C_6\\\\C_7\\\\C_8 \\end{array} \\right] = \\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]$$\n", + "\n", + "[Hier](rref_ti_84) is uitgelegd hoe je dit op een grafische rekenmachine kan oplossen.\n", + "\n", + "Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt.\n", + "\n", + "Beide opties zijn hieronder getoond" + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAs9JREFUeJzl102o1FUYx/HPaEh2q3sxCgKtqNRdS7tgQi8g0SZXLgLBCiEsYnITFPTwBLqMUBRq0S2iVhEGtRF6WVTULktLbwTtgsDK6nbpzXFxztTcaeY2M3eGS/SF4YHznJfn9z/nPOeZRqvV0ovM3IwHsBPXYQY/4CRex4sR8UvPwatEZm7E07gbV+EbHEdGxPftfo1u0ZnZQOJxrMOH+BTncb3yETbg/YjYMWkhg5KZNymxXoM3cAbbcAfOYntEnINLeox/AXvxOe6LiJNdk6/HAdw8ofhH5Zgi+NGIONJuzMxn8BgO4iFY0zkqMw8ogr/AbLdgiIjFiDiI/ZOKflgy80blBH6No13uwAL2ZOYUHaIz81rla/yB3RHx03ILRcTi+MJeMXdWeyIiLnQ6qo4PcBlmWXq8m7gUcxFxatJRZmZTSY6D8klEHO/j21rtfB//l8pJ2IK3O0XvqvblIQJZCU0lMQ7KS0om7sV0tef7+NvtM9SdzszLla/QwsdDBPIXmfmEcj2ORsQj/9Y/Im4YZZ0RaVTb4u87fXW1P47y9mbmLPYpT9tq0N7J6T7+Kzv7tY/3QrXrM3NtRPw56GqZOY1X8CCeGmJc0/ju9Nlqt/Txb652nrrTEfGtku7X4fblVs7MNV1Nz+O1iHhnuXE9aCrPyaC/XcvM9W61O7vjy8wrsB2L+Iil2fvZ+juWmfdGxJmuwQ2lvLsfu2vbPqVI2TOw1Mo473REfJWZJ5QM/TCOdLgTU3guIhZYKvowblHq7c/qJPP4HZtwGzbiVcjMrTiEHRHx27gErID9Shl6ODPvUgqsW5UydB5Ptjv2qr3vUZLSNiXB/awU7qfxFt6MiHOZuRdz6Lz/a5UMeQFTEfHrBMT1JTM36f+H47t2v3+IHmKBGWXnO5lTCoFDOB0Ro00+YUYW3YvMfA+nBnmnV5PuTPy/YKw7/V/hImUh8qiSnFTZAAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle C_{4} = 0$" + ], + "text/plain": [ + "C₄ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAt9JREFUeJzV10+IVXUUB/DPTBGZmWIYBFpSaruWJZmRBSZtkhYGkZBBUAoxSRDS4nACV1HISANt+kPQMoxqIw1tKmqn/TGdCNwFQX+smYbIei1+99ad17vje773kL5wObx7fud3zvf3O+e8cyc6nY5eyMzNeAw7cQPW4GecxNt4PSJ+62l8iZCZ6/E8duFafIdjyIj4qV430U06MyeQeBZX4BN8jnO4UTmEtfgoIraPm0i/yMyblVivwzs4jduwA2ewLSJ+gMt72L+KR3EKD0fEya7NV+AgNo0p/ovFjEL4qYg4Wr/MzJfwNA7jCZhsWmXmQYXw19jaTRgiYjEiDmP/uKIfFJl5k5KBZ/FylzqwgL2ZuZIG6cy8XjmN89gTEb8u5ygiFkcX9tC4p5LHI+KvpqLi8TGuwlaWpvcUrsRrEfHluKPMzCmlOfaLExFxrEV3SyXnWvTfKJmwBbNN0rsr+eYAgQyDKaUx9os3lE7cC6srea5FX79fQ3XTmXm1cgodfNZvFJl5CA8qJ/07PsWhfjIlIjb262cEmKhkh39rel0lfxnwv/dupWveodTVeXyQmWuHj3Mg1De5ukV/TXNdnd4LlVyRmZdFxJ/9eIqI+5q/M3NvtfE2vLuc7Yhr+kwlt7ToN1dyjop0RHyfmWexUbm92TbPmTnZ3SEbWKVkz49t9g1MGV1Nf1jJnd3xZeYq5RIWlfJb0r2PVM9MZj4QEaebu1aT2i7sw54W50dwot58OYyypiPi28w8rnToAzjaUCdW4pWIWGAp6WncqszbX1SbzOEPbMCdWI+3ejnOzBdwF7b3Wx4jxn5lDJ3OzHuVAet2ZQydw3P1wl6z9/14XJlb12FeGdy/wvt4r55hGzYv4hHsiIhT4+F0YWTmBu0fHP+U3H9IX4SjaTzkEhMeBEORzswZ5YZ3Kx8oNeYjYn640MaHyQsvWRZPKh17Vkml+nlmyH3HiqHT+/+IvwHtQ/pzJbnwjAAAAABJRU5ErkJggg==", + "text/latex": [ + "$\\displaystyle C_{2} = 0$" + ], + "text/plain": [ + "C₂ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAa8AAAAUCAYAAAA6EbIDAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAADP1JREFUeJztnXewVdUVxn+IscRGorEkGhUjUWwwo4AoiILEGt+zTWIsWFBHDXmWoViy+HRQnKggKmosoEaNiQoGxTqixJ4RsaFi4VkRFMVgj0L+WPvA4XLOLefe++59yf1m3mzO2eWsu9faa++91tqbDkuWLKFakHQEcGN4HGxm1xYovyVwDDAQ+DnQCVgIPA/cCUw0sy8z0LEB8AFwhZkNCe8OBnYDugE7AGsBN5vZ4QXa2hf4A9AVWBeYCzwLXGJmT+aUHQRMKEDeYjPrGKvTCmyaUnaemW1YoL2qoy35FMvrA7QAvYEfA58ALwJjzWxqKLMu0AzsC2wH/Az4NpSbAEwws8UJ322l/vu8LsZSW6G90l+Kfsipl6SjLgR2BLoA6wFfAW8Dk4HLzWxBrP4gStQ1oV4rdSj7kjYGzgX2Ylk/TgZkZp8CrFzFj28CXAZ8DqxZoGwHQMAwYBXgCeB24DO8YwcC/YHfAn0ykHMAsBIwKfbubHzS+hx4D9iqUCNBmIYCC/CO/Bj4RWj/IElHmtlfYlVmht+VhD7AHsC9CXmfAWMT3n9eiMZqokZ8QtLZwHl4f9+NC/J6QHegHzA1FD0EuDLkTwPeATYADgSuBfaWdIiZJa3Y6rLPoe7GUlXRnunPoB/iSJL9U4EZwIPAfGANoBcwEjheUi8zezeUnUk2XQN1JvuStsD5vj5wF/Aq0ANfFOwlaRczW1CVySsI4ASciXcCZxSocj0wCJgFHGZmz+e0tzpwGi4IWdAcaJkee3cqPmm9ge/ApuVrQNKG+O+YB2xvZvNjebsDD+MrhaXCaWYzcaFKai9ahf05IXuhmY3MR0+N0OZ8knQIPnE9BBxoZotyvvmD2ONs4NfAPfEdlqQzgWeAg/CJ7I6Eb9dln9fhWMqL2A5gdzN7JEMTNaO/HNqz6IccJOmotc3s64RvjQLOBEYAJ0FZugbqT/bH4xPXEDO7LHop6RJcb48CTqzWzmsIPtP3C2kqJJ2GC+srQK9c5QRgZl8Bo4LglgRJ6wQabjGz72NtTouVKaapTfGV0dNxwYzakrQI+EmRNG2Lr6DeB+4ppk6tUQs+SVoJuBD4EldkSd/8T+zfDye1bWYfSroKF/p+JE9e9Yq6GUvVRjunP7N+yKOjVpi4Av6GT15bFiKqvekaSZ3x3XUrcEVOtgHHA0dIOr3ik5ekrYHRwKVmNl1S6oCTtBGuUL4DDk0S1jiC4JaKfXHzw50Z6sbxOu4/6SFpPTP7OMqQ1Bf3mU0usq0TQnpdXFhjWFXS4bit/wvgBWB6Stmqo4Z86g1sjpuNPg3+hG2Br4Fn8vkQEhBNct+l5NdVn0NdjqWqob3TT3n6oVQdtX9IXyiibCFdA/Ul+5GMP5DrnzazRZIexye3XhWdvCStDNyE+xrOLKJKC7Aa7kh/qZK0xNCMM+TBchoxs08kDQMuAWZJmoxv87fATVUPskxQUhFWjIcDi3E/TBI2xPsxjjmSjjazR7P9grLQQm34tFNI5+G2/+3iFSRNBw42s4/yNRzk8sjweF9Ksbrq8zodS9VEC+2Y/jL1Q14dJekM3Ne5Dh7AsSs+wYzOR1ORugbKlH1JLXhATbGYaWaTU/J+GdLZKfmv45NXl5VK+GAx+CPuRB9U5MqoKaS5HVcRSFoNj1a5N88WvGiY2VjcZ7IyMBgYjgcJvItHP81Pr70Uh+KMvjfmbI1jAu6Q3hB30G4HXA1sBtwraYeyfkQ2NIW0rfm0fkhPBFYHBuAr2G2B+4G+wN+L+MToUGeqmd2fkF+PfV5XY6kN0BTS9kp/Jv1QpI46AzeZteAT133AwEKLNgrrGqiM7LcE+or9a8rT1joh/SwlP3rfabmdV4GwySQsDS2X1ANfIV5cjDlH0pp4COgS4OkSvomkkXgnxJEU1jkQX7FMogKQNBQ4HxgHXA58iEcpXgDcLKmbmQ0t0MzxIb06KdPMch1wLwEnSvocOB2PNGouQGcrGfmY0FZmPoX6G+GTxz44L94ATslZ0aXxKQrr7YDvsCLn/cuSmvHV2W6Sdk6TOUlD8H57FTgiqUwd9nlbjqVWkukeb2YnZ6wLMC3Bl3yDmQ1KaKcc+jvi/Dkc2AiPNL0ZGGlmaSbiitGe02YW/VBQR0V6LYTT98bH03OS9jOzGXlIyqtrQttly76ZbZYvv8LoENIluWbDN3F/QrH4AJYzccwGzimybuS8/HfG8xqv4U7sCEn22WbcDl22o1JSPzx4YJKZnRbLmhFTpKdLusrM3kppoysufO+xLLy7WFyFC1PfIspm4mMKMvNJUifgceAx3K7/EdAZD/uNI41Pn4b0rdyoMzP7StL9wLF4GO0KSl7SycCleORafzP7pBT6qUGf12As7cSyRQL4DvVBitvRjmVFc1E3POz7BtzpHsfMlHbKoX8YcDJwFH6eb3tgIvANHqWahrFUhnagLP1QtI4ys3nAJEkzQns34vxKoqccXQOlyX4lEe2s1knJXzsqt9zkZWb9M34wWjkBfJ0SvXeNpGtw53MLbuMFWF1SxwzOwe/M7MO0zLAi2x942MzStqClYL+QrhBSb2ZfSnoGF8TuQOLkRXHO0zRECn+NQgXL4GMSyuHTUGCumR0ZezcnXqAAn14L6cKU9qPJbYXIs2CHH4OvJPsXadLNRS36vE3HUq7pSdJwfCIu6OsIZrLlEMLND8DNZI8USUY5MtYb+IeZTQnPrZKmAD3zVaog7RFK1g9ZdZSZvS1pFtAtNzgkhnJ0DZQg+xX2eUVjvktKfhRhObtSPq9vgOtS/p4LZR4Lz08CBGXSikfZ9MvXeAiZzkVnSe9LmiPpVkmb5eT3xU9mV8RkCKwa0rRw+Oj9t0mZwbZ9BO48vS7D93cOadrEWBWUyacm4ClJt0iaL2mmpFPkZ5ci5OPTdDz6bEtJqyTkR6vO1hw6huET10z8zE6WiQtq0+e1GEtR3iq4+e16Sz7IXRWUSf8TwB6Stgr5XfGItSy7jXKQRT+Uo6N+GtIVJqYK6BooTfZbqJzPK5r8B+byWtJawC74TSNPVSTaMDiUj0vKC/6p7rjNODfiZWz4Gy/pADN7NaduB9yZeTTufIzwNM6c2fgNCiOAJyVtEzMNHYgz767MP2x5/BM4BT/ZfrWZvR+jc2+8U7/GB1MSDgF+BNyd5jyVtA2+U/kk5/2muA0d0g85VhNjycanzrhJZwzwK9wscxnu24jOcKTyycw+lnQb8Ds8gOHs2Df3DG1+RiyCUNI5+GHQZ3Gndl5TYb31eQ3GUhxN+Ap6Yhbay8RYstE/Gg/imSXpezxYYpSZjW8LomPIoh9SZT9MxgtzrUtBoZ+HBzM9YeGqpBwU1DWhrYrIfiV9Xmb2pqQHcF/gybi+WEoavhO82sy+qNr1UEViHG6jPgZ4MRA9Gz+TswkeWbMxcEu8kpnFrzl5UX6C/C38gOMl4X0Tztx5SR+W1MSyFUAU6LGzpInh3x+bWfw2g9vxWx4GAK9ImoQ7ZLfGTQYdgOEWu28sB5HzNO2UO7jQDZc0DTevLcJDbffFw4inAhflqV8tZOITfmjzWTMbEZ6fk9QFF8po8moiD5/w2xR6AmfJz8s8gzvam/FV52AzWwgg6Sh84voeVyZDEsxurWY2MfZcr31eKrLyKI5j8ci0fD7QaiEr/Qfji5vDgJfxBdKlkuaYWdZdRxZk0Q9NpMv+XsCf5MdB3sTD7jfAbwPqHNoenEJLMboG6lf2T8In+XGS+uOH1nsCu+MycRZU8W7DYhBME8dKugNnRA9gT/xOrbm4WeQe/D67fO18EWzAWwJI2gkX9DF5qnXDnbxxdA5/4BdgLp28zGyxpH1wxfsbXHn+EL8gdiowzsweSPqQ/LDprhR2nk7Dzzl0x7fta+D+nsdwJ/5NbWnOiVAGn+biCiWOWfitEUXxyczmS+qJ77qa8dsCFoXvXWBmT8WKbx7SjrgpIwmPsvzOoi77vFSUO5bCansAvhtoc5RB/8XARWb21/D8YvgtZ5LdZFYyStUPRcj+Q/jkswt+B2sn3Dc4G5fLcUlWhRJ0DdSp7Ifd144su5h3H1wGxgGKfneHat4q31YINt45wJVmdq6k83FTYmczm5O/dgPVgqRbgE3MrE/s3XnAQWbWtcGn+kEwSZ6A8ys1xLzeIGkBYGZ2eezdCOA4M9uidpTlR0P2y0etzYaZIOkiYAp++8D6eEjxGniIK/iq5/mGUNQcY4AnJJ0F3Iav8Iaw7MaIBp/qAMGPcjTuS2s3E1fAFNz0NQff5XfHTc035q1VezRkv0y0y8kL327fiv+3GB8BT+EXeb4NYGZb15C2BgLM7F/Bt3g+vsB4J6TjQ36DT/WBAfi9dtfXmpAM+D0ewBDdRD4XuAY3OdUtGrJfPv4nzIYNNNBAAw38f6HSdxs20EADDTTQQNXRmLwaaKCBBhpod/gv8eISpCWMfDQAAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle - \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0$" + ], + "text/plain": [ + " 4⋅C₅ C₆ 8 \n", + "- ──── - ─── - 4⋅C₇ + C₈ + ─── = 0\n", + " 1875 625 375 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0$" + ], + "text/plain": [ + "C₅ C₆ 8 \n", + "─── + ──── + C₇ - ─── = 0\n", + "625 1250 375 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY4AAAAUCAYAAACajtrtAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAC9JJREFUeJztnHu01UUVxz9XTDE0KE1xpYGSDwgVlspDA0EUH1ReVCwNFJ9hGt20RFDbfDUVl68rKomPAE1XrVAwFHyCkuGjNBRFxQc3X4ghQoCiCfTHngM/Dr/feZ97Dq3zXeuuub+Z+c3sM/s3s2c/ZurWrVtHuSBpCHBneDzTzG7PUn8P4DSgP/BtoA2wDHgRuA+YaGafFkDHTsAHwM1mNjzkHQ8cAnQB9gO2A+42s8FZ2hoA/ALoBGwPLAKeB64zs6fT6g4FJmQhb62ZtYi80wS0S6i72MzaZmmv7GhOPkXKegENwEHAN4ClwDyg0cymhzrbAwOBAcA+wLeAL0K9CcAEM1sb028T1T/mVTGXmgs1+isDSbsAlwJHsmF9mwrIzD5J1duyjATsCtwIrAS2zVK3DhAwAtgKmANMBpbjE7o/0A84EehVADnHAFsAUyJ5F+MCYyXwHrB3tkYkXQVcAHyMD+YS4Duh/eMknWxmf4i8Mjf8rjj0Ag4FZsSULQcaY/JXZqOxnKgQn5B0MXAZPt4P4B/zDkBXoA8wPVQdBPwulM8C3gF2Ao4FbgeOkjTIzOJ2S1U55lB1c6msqNFfOUjqgNO7I3A/8BrQDd8oHynpYDP7GMokOMLgTcAX2PuAX2V55ffAUGA+cJKZvZjW3jbAefgiXQgGBlpmR/J+iQuMN3HNY1amBiS1xX/HYmBfM/soUtYXmIlL6vWCw8zm4sIjrr2UdnJrTPEyMxudiZ4Kodn5JGkQLjQeA441sxVpfX4l8rgA+CHwYFSzkDQKeA44Dhci98b0XZVjXoVzKSMiWnZfM3uigCYqSn8JsDnTPw4XGsPN7MZUpqTr8PXycmAYlE/jGI7vpvuENBGSzsMH+lWgR/rCAGBmnwGXh0HPC5JaBxruMbM1kTZnRerk0lQ7fDf8bFRopNqStAL4Zo40dQZ6AO8DD+byTqVRCT5J2gK4CvgUn4Rxff438v/MuLbN7ENJt+Affh/iBUe1omrmUrlRo79ykLQ7rg01ATenFRtwFjBE0vlmtqrkgkNSR2AMcIOZzZaU+LFL2hmfzF8CJ8QNdBRh0PPFAFxlvK+Ad6N4A7eXd5O0g5ktSRVI6o37SKbm2NZPQ3pHVJhFsLWkwbhtdBXwEjA7oW7ZUUE+HQTshqv6nwT/UmdgNfBcuk8pC1IC5suE8qoac6jKuVQ21OivOFLf1iPpfkAzWyHpb7hg6QE8XlLBIWlL4C7ctjwqh1cagJa40/LlUtISwUB8IXi0mEbMbKmkEcB1wHxJU3HzQQfcPPIoGwRCIsJOYzCwFre7x6EtPo5RLJR0qpk9WdgvKAoNVIZPB4Z0MfAC7vBeD0mzgePN7N+ZGg7f5cnh8aGEalU15lU6l8qJBmr05wxJDbjDPVfMNbOpGcr3CumChPI3cMGxJ/D4Fnl0nAt+gzssh+YoUetDmj5hSwJJLfHogBlmtrrY9sysEbeRbwmcCVyIO2TfxaMkPkp+ez1OwBk+w8zejSmfgDvP2gKt8MVyPNAemCFpv6J+RGGoD2lz82nHkA4DtgEOwzW7zsDDQG/gzzl0MSa8M93MHo4pr8Yxr6q51AyoD2mN/tzQgJuQcv2rj2skgtYhXZ5QnspvA2k+jixhiXFYH74qqRu+M7o2FxOCpG1x6bUOeDaPPlOmoV8D+wM7A4PMbHJM1f54FMqUmLK8IekC4ApgLHAT8CEejXUlcLekLmZ2QZZmzgrp+LhCM0t3uLwMDJO0EjgfGI3vzjPR2USBfIxpqxg+jcQF7V7A58AzwMiYHVkSn1JhynW4ZpFyNL4iaSC+OzpEUs+kb07ScHzcXgOGxNWpwjFvzrl0Dq4ptw9ZrwC/NbOsvrcsv3lWjO9wkpkNjWmnYPrT2hmFm4tuNrNzs9RtojrmSAv8+xqMr2WLgLuB0WaWZFbFzNrn008JUBfSdbCpc/wt3H6cKz6AjdTqBcAlOb6bciT/p4B45lZ4PPQEMjs6B+J+iaId0JL64I7aKWZ2XqTohcgidr6kW8zs7YQ2OuF2+/fYEEKaK27BF7HeOdQtiI8JKIZPffBIjb/jH96lwGOSOpnZ0ki9JD6l4sbfTo9OMbPPJD0MnI6HDG6ywIZF8QY8wqVfWp+5oNnHvAJz6T1cc34D59EpwFRJ+5vZS1nebWRTc0kXPKx6Eu5ojWJuQjvF0A+ApB64FSAbzSlUyxwZAZyDj/s8YF9gIr7RuizPtopBSqNonVD+tWi9jQSHmfUrsNOUxAVYnRCldJuk23BHXwNuzwbYRlKLfJyQZjaDcP4hKSIqSPIfADPNLEn9ygffD+kmYbtm9qmk5/AFsCsQKzjI7hTPhJQZrFW2ikXwMQ7F8OmI6HM4xLYcOBiYFvIy8en1kC5L6CIlWDaJUAk24Otx7aFfjmbEdFRizJt7Lt2flnWRpLOBnmRZhIPpdiOEcNxjcNPtEzmSUTD9oc/W+C79dNzElxXVMkfwjeRfzGxaeG6SNA3onumlMvg4UnNtz4TyPUK6ADy8tBT4HLgj4e+foc5T4flpgDCRm/BImj6ZGg9hmfmiN37ysSRmKmDrkCaF3Kbyv4grDHb8IbhT/I4C+u8Z0iShVBaUmE/b4d9cdOefiU+z8SiVPSRtFVPeOaRNaXSMwIXGXPw8QSFCAyoz5hWbS5JaSDoRF15zivgNeaEE9N8KTE4Kxy43iqR/DnCopL1DeSc8wimbRaKB0vo4Uhvi/uk0StoO3+x9hpubS3OOIzjvzogrkzQa34VPsk2vSWgMf+MkHWNmr6W9W4c7TU/Fncr54Fh8kU7fURWKvwLnAmdJGm9m70foPAof2NUkT7hBwNeBBxKc4kj6LrAo3aQiqR3uU4HIAcNmRCOl4VMjvpg/E8lL5JOZLZH0J+An+E7y4kifhwNH4BrMQ5H8S3CT2PNA/2zmqWob80rMJUn74EKoJbACqDezecX+ljzRSAH0SzoTP0wX679qRjRS2PiPwTdU8yWtwdfky81sXKbOSu3jMLO3JD2C+xvPwW8qSEG41j3ezFZBGa8cyRFjcZveacC8QPgCPOZ+V+B7wC7APQW0XQ/MMbPFcYWS6tkghVN3EfWUNDH8v8TMoqd0J+Onlw8DXpU0BXeOd8TNWHXAhRaO5Mcg5RSPOymewiDgQkmzgIX4JO6An3Foie9CrsnwfrlQNJ8kXY1rF73SVPl6MvAJP2XbHTeh9MZPgLfDzYJr8HubloU+TsGFxhpc0A+PMfU0mdnEyHO1jnm+KIZHr+O+iTb46fo7JfVp5rDYvOmXtBcerNLLzGI1/WZEoeN/PL4xOgkPTOgC3CBpoZkVYpkoBj/DN75jJfXDDzJ2B/riv+WiVMWKCg7zO4NOl3Qv7tjqBhyO38mzCN8FPYjfT5QzJB2IM+n6DNW64A6pKHYPfwD/InK9g5mtlXQ0Lo1/jC9cX8XNLtOBsWb2SAI9HfEPJ5tTfBYegdQVN5O0wu37T+EO07ss/p6lsqJYPkm6Fo8a6Wtmb0bys/LJzD6S1B3XNgbiB5BWhP6uNLOo9rJbSFvgqnwcnsSdjylU5Zjni2J4FBbdFF/+EfjSQILmUw4USH9P/M6ylyMbhBZAb0nDgFZm9nkV0w9wLXCNmf0xPM8L2u4oCjNpF4ygdRzAhksOj8ZpH4tfcrheK68r5+24zQFJ60gLx5V0BTAS2N3MFlaMuBqQNBb4ES405qeV1fhUhZA0E/ggKfy0WiCpDb7xiGICHiF2BfBKtQt9SR8DZmY3RfJGAmeYWYfKUZYZlTZVFYQQNx29JKy9pC7AUjN7B9+ZvlhbjCoLSeNwTaMeWCq/KBJgpZmtpManikPSGHwn/C5uaz8Jd/AOqCBZOSGYKJdF8yStwteBzeX0+TTcVLoQN1V1xc2zd2Z8q8LYLAUHcAAbh8VeHdJJ+Enbjs1PUg0xODukj6flCz/gVONT5dEWd/63xQMNXgKOsvgT9jWUHj/Hz2ukbqZdBNyGm4uqFpu9qaqGGmqooYbmRanvqqqhhhpqqOH/HDXBUUMNNdRQQ174Hyg4Ej1GSXI4AAAAAElFTkSuQmCC", + "text/latex": [ + "$\\displaystyle - \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0$" + ], + "text/plain": [ + " 4⋅C₁ C₂ \n", + "- ──── - ─── - 4⋅C₃ + C₄ - C₈ = 0\n", + " 1875 625 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0$" + ], + "text/plain": [ + "C₁ C₂ \n", + "─── + ──── + C₃ - C₇ = 0\n", + "625 1250 " + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAUCAYAAAAdtqaHAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAABSNJREFUeJztmmuoVUUYhp9jIpmaQhQIWqFp5A8pMjO8kB5QqR8eioxCRbtAqdjJbmjY62cYgVhHJSG01CQhsDAKBFG0sDQkVDKzk5L4I8HQsjSVLrsf36zarc5ae+3bWUfYDyxm77m+s5j5Zuab1VQoFEjCzKYD74S/j0tam5J3CPAIMBG4HugH/AwcBD4A1kv6LbGxnGnozwczGwAsASYD1wAngS2ASfopytc9pYKBwCrgHNA7JV8TYMALQA/gc2AzcBa4AX9xzcBDwNgq+lQXGvrzw8wG43qvAz4EjgAjgaeAyWY2WtJpSBioofPrgNP4bHw2pb23gZnAYeBhSQdjdfUE5gM3Vd6lZMxsZtA6XtKuCqrIVX8NuJz1r8YH6TxJq6JIM3sNeBpYCjwB0C2hgnnABGAWcD6pFTObj7+kb4BR8ZcEIOmCpKXA7Ep6Uk8a+vPDzAbh1v448EYsWfi4m25mvaCDgWpmtwCvAiskfZrSUH98xP8BTJX0a5owSReyd6P+NPTnzoQQbpP0V3FC6MtnwFXAKIgt/WbWHdgInAAWlmioFbgSWCfpUNWyO59WGvozY2at+AEtKwckbUlJvzmE7Qnp3+EWdyiwI75HfQm4DRiTYQa2hHBjiXxdlZYQNvRnoxU/nGVlA356T6JvCM8mpEfx/aDIoprZSNyKLpe0J02BmfXGR3oB+KKU4ljZccBzwO1Af+ABSZszlj1O8svaaWbxuA2SZnZQTzX6FwD34RbhErAXWJDFqpXQ3xHvSprWQT0V6w/l++Pbu3twj85RYK6kT5LKSLqx3HaqpCmEBQgDtWjJbwcWZajk2hD+UoFvrhfu21sHvF9m2Tb+v/zcCkzBZ/DxWNqBhHqq0X83flrdh7/MJcB2Mxsm6UyJsseAi2W09UNCfMX6zawfvv/bDdwL/AgMAk6VU08NiCxm34T0q4vzRRY1mqEAFzuwTABrzGwNsAJ4JcT1NLMrJP2ZVZ2krcBWgIR20sq2xeOCe2oK7tDelbGqyJNRif5Jsfan4y9zNPBRibLNWdspQcX6geeBk5JmFMV9X6pQHfao34ZwaEL6kBC2w7+n/kvAWwnP/pBnd/i/R9Ip3Hr1wC1MImaW5ALLjRrr74O/x1LWtGZUqb8F2Gtmm8zslJkdMLO5wXeeRivuNsr6tJSob2cIJ8Y1mlkffOJfwLdWblHDwemxhI4uxg9YG2JXqG3hWW1mUyQdiZVrwq/FZgFTS4jOgzZqo78N32LsrYfIEu22Ub7+QcAc4HVgEr51WoXvBeP+zH+o9R5V0jEz24af7OcEDRGGbxHflHQeUq5QM7ASGI7fL38VGm0HfgcGAmOAAcCmKtqoJ1XrN7NlwDhgbJnLby2oVH834EtJC8L//WY2FB8siQO1TszGr1BXmlkzfnFxJzAe78uLUcaKl2VJBUmP4hvyj/GZOQe3zMOBPfitybxK26gn1eo3s+XADKBZ0tFOkPwfqtB/Evg6FneY8rwRNUHSMWAEsB4foM8Ag/FJeFd0zw/QlPb1VL0xswJluKe6Cma2EngQ/77gcN56ysHMNgEDJY0tinsZuF/SsPyUpdPpAzX4AKMPJPbjPtXtwBlJJzpVTAWY2WpgGn5YKB6k5ySdy0VUGZjZHfhyuxh4Dz9/rAUWSurspT8zeZzIR+ADNPImLAu/l+SgpRKexE/6O/BlNHrSvjDrMkjah0+yqcAh/HuBRbhvuMuS69LfoEFWupyPs0GDjmgM1AaXBX8Db+ZLKnj2sa4AAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle 4 C_{1} + C_{2} - C_{6} = 0$" + ], + "text/plain": [ + "4⋅C₁ + C₂ - C₆ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAUCAYAAABs4ghcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA6lJREFUeJztmV2IVGUYx3+zimhWCn0KWy2adhGEkNSGFdWCCF20NwlFQqUXYmCTEhEh//6CV4aIktFNukR5Y2EUCEJ4VZt3iWF+sPkBfZOlohukTRfve7az48x4zsyZ2R3YHxwezvv5f+Y57+eUKpUKtbC9EHgZWAbcDcwF/gIOA58CuyVdrll5gulW7bZ7gU3AcuAW4GdgH2BJf6bLTq9RuQQYeAOYAXwN7AXOA/cQfowB4DngsXY50Qxdrn0BQe/twGfAMeAh4FVgue2lkv5Iyl8TOOAD4EXgKPC8pMNVHcwC1gP3tsOBFulm7TsJQVsnaUeSaHsr8BqwGViTpPeka9peT3D8e6C/2nEASaOSNgNr26G+Wbpc+3zCbHAaeLcqW8AlYKXt2UliT6ryPEJUrwArJF1s1Jmk0WJkt043a488Fe0BSf+mM6IvXwE3AP1JenqqLAMzgV2SvmuvzsIp02HttsuETU9WvpW0r07efdGeqJN/kjAiFwFfwvjADUb7YQ4xk4XBaDupvUzY8GRliLBDrMWcaM/XyU/S5yYJ0wFs30iIZgU4lFWJ7ceB14EHgXnAs5L2Zqx7mnyOfyTphRrtNKU91n2bsIak+VXSnderK6kvT18tUop27OyWjLjbor2Q83wzm3A22gV8klPMCPB3jvI/1UlvVnvCceCJ1PvVJtpolWREzamTf3NVubHAXYp2lu1pkjKJl7Qf2A9gO5dSSQO5KtSnKe0prkj6JW+nBa9xx6NdVCd/YbRja2APgKTfCFvRGYz/+q7Bdk+j/E5TgPb5tn+0fcr2Htt9GbsuE6bZrM9gg7YORrusWqPtm4ClwCjwTZKe3pxsi89O289IOlbVQIlwFfMSsCKjc51iG81pPwSsJHzJdwBvAsO275d0rlGHRa5xkkZsHyDsHF8BdqSyTViS3peUzC7jArcdeIBwx3ckNnQC+Ae4C3gU6AU+LkpwgTSlPU71CUdsDwM/EA7yW9svexxrCVde220PEC4SHgaeJPjyVrrw2LCUVJG0Cnga+AJYTIj+asKPMkxwaF27PchLUdrjF32U/9eUjiFpBFgC7CYEbAOwgPBRPpK+pwQo1ft3IC+2K+Q4DkxGbM8ETgHvSdo00XoaUeuSOTPxDJW+sO2zvRg4J+lsK213AtvvAJ8DZwkXvBsJ68nQROrKQkuBIwztg6n3LdEOEaamyU4vsAe4FfidsGvrl3RmQlVloLCpcorOMqnOZFNkZypwXcp/TvVqwtp6oi4AAAAASUVORK5CYII=", + "text/latex": [ + "$\\displaystyle C_{1} - C_{5} = 0$" + ], + "text/plain": [ + "C₁ - C₅ = 0" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C_{4} = 0\n", + "C_{2} = 0\n", + "- \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0\n", + "\\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0\n", + "- \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0\n", + "\\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0\n", + "4 C_{1} + C_{2} - C_{6} = 0\n", + "C_{1} - C_{5} = 0\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "import numpy as np\n", + "import matplotlib.pyplot as plt\n", + "\n", + "%config InlineBackend.figure_formats = ['svg']\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = 10 \n", + "EI = 5000 \n", + "EA = 20000 \n", + "F = 150\n", + "\n", + "q_AC = 0\n", + "q_BC = q\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", + "\n", + "V_AC = -sym.integrate(q_AC, x) + C_1\n", + "M_AC = sym.integrate(V_AC, x) + C_2\n", + "kappa_AC = M_AC/EI\n", + "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", + "w_AC = -sym.integrate(phi_AC, x) + C_4\n", + "\n", + "V_BC = -sym.integrate(q_BC, x) + C_5\n", + "M_BC = sym.integrate(V_BC, x) + C_6\n", + "kappa_BC = M_BC/EI\n", + "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", + "w_BC = -sym.integrate(phi_BC, x) + C_8\n", + "\n", + "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", + "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", + "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", + "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", + "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", + "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", + "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", + "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", + "\n", + "display(eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8)\n", + "for eq in [eq1, eq2, eq3, eq4, eq5, eq6, eq7, eq8]:\n", + " print(sym.latex(eq))\n" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle \\left\\{ C_{1} : \\frac{35}{8}, \\ C_{2} : 0, \\ C_{3} : - \\frac{1}{150}, \\ C_{4} : 0, \\ C_{5} : \\frac{35}{8}, \\ C_{6} : \\frac{35}{2}, \\ C_{7} : \\frac{1}{3000}, \\ C_{8} : \\frac{13}{750}\\right\\}$" + ], + "text/plain": [ + "⎧ 13 ⎫\n", + "⎨C₁: 35/8, C₂: 0, C₃: -1/150, C₄: 0, C₅: 35/8, C₆: 35/2, C₇: 1/3000, C₈: ───⎬\n", + "⎩ 750⎭" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "'\\\\left\\\\{ C_{1} : \\\\frac{35}{8}, \\\\ C_{2} : 0, \\\\ C_{3} : - \\\\frac{1}{150}, \\\\ C_{4} : 0, \\\\ C_{5} : \\\\frac{35}{8}, \\\\ C_{6} : \\\\frac{35}{2}, \\\\ C_{7} : \\\\frac{1}{3000}, \\\\ C_{8} : \\\\frac{13}{750}\\\\right\\\\}'" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "display(sol)\n", + "sym.latex(sol)" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " 2025-07-02T16:58:07.214407\n", + " image/svg+xml\n", + " \n", + " \n", + " Matplotlib v3.10.3, https://matplotlib.org/\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "\n" + ], + "text/plain": [ + "
" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "M_numpy = sym.lambdify(x, M_BC.subs(sol))\n", + "x_numpy = np.linspace(0,4,100)\n", + "plt.figure()\n", + "plt.plot(x_numpy,M_numpy(x_numpy))\n", + "plt.xlabel('$x$')\n", + "plt.ylabel('$M_{BC}$');\n", + "ax = plt.gca()\n", + "ax.spines['right'].set_color('none')\n", + "ax.spines['top'].set_color('none')\n", + "ax.spines['bottom'].set_position('zero')\n", + "ax.spines['left'].set_position('zero')\n", + "ax.invert_yaxis()" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\left[\\begin{matrix}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{matrix}\\right]\n", + "\\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]\n" + ] + } + ], + "source": [ + "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8), (C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "print(sym.latex(A))\n", + "print(sym.latex(b))\n", + "A = np.array(A).astype(np.float64)\n", + "b = np.array(b).astype(np.float64)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "A = np.array([\n", + " [0, 0, 0, 1, 0, 0, 0, 0],\n", + " [0, 1, 0, 0, 0, 0, 0, 0],\n", + " [0, 0, 0, 0, -4/1875, -1/625, -4, 1],\n", + " [0, 0, 0, 0, 1/625, 1/1250, 1, 0],\n", + " [-4/1875, -1/625, -4, 1, 0, 0, 0, -1],\n", + " [1/625, 1/1250, 1, 0, 0, 0, -1, 0],\n", + " [4, 1, 0, 0, 0, -1, 0, 0],\n", + " [1, 0, 0, 0, -1, 0, 0, 0]\n", + "])\n", + "\n", + "b = np.array([\n", + " 0,\n", + " 0,\n", + " -8/375,\n", + " 8/375,\n", + " 0,\n", + " 0,\n", + " 0,\n", + " 0\n", + "])" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "array([[ 4.37500000e+00],\n", + " [ 0.00000000e+00],\n", + " [-6.66666667e-03],\n", + " [ 0.00000000e+00],\n", + " [ 4.37500000e+00],\n", + " [ 1.75000000e+01],\n", + " [ 3.33333333e-04],\n", + " [ 1.73333333e-02]])" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.linalg.solve(A,b)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": { + "tags": [ + "remove-cell" + ] + }, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\displaystyle \\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]$" + ], + "text/plain": [ + "Matrix([\n", + "[ 4.375],\n", + "[ 0],\n", + "[-0.00666666666666667],\n", + "[ 0],\n", + "[ 4.375],\n", + "[ 17.5],\n", + "[0.000333333333333334],\n", + "[ 0.0173333333333333]])" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]\n", + "\\left[\\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 4.375\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -0.00666666666666667\\\\0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 4.375\\\\0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 17.5\\\\0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0.000333333333333334\\\\0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0.0173333333333333\\end{matrix}\\right]\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "\n", + "A_sym = sym.Matrix(A)\n", + "b_sym = sym.Matrix(b)\n", + "\n", + "sol_sym = A_sym.LUsolve(b_sym)\n", + "\n", + "display(sol_sym)\n", + "\n", + "print(sym.latex(sol_sym))\n", + "\n", + "identity = sym.eye(sol_sym.rows)\n", + "rr_augmented = identity.row_join(sol_sym)\n", + "print(sym.latex(rr_augmented))" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "C_1 = 35/8 ≈ 4.37500000000000\n", + "C_2 = 0 ≈ 0\n", + "C_3 = -1/150 ≈ -0.00666666666666667\n", + "C_4 = 0 ≈ 0\n", + "C_5 = 35/8 ≈ 4.37500000000000\n", + "C_6 = 35/2 ≈ 17.5000000000000\n", + "C_7 = 1/3000 ≈ 0.000333333333333333\n", + "C_8 = 13/750 ≈ 0.0173333333333333\n" + ] + } + ], + "source": [ + "import sympy as sym\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = 10 \n", + "EI = 5000 \n", + "EA = 20000 \n", + "F = 150\n", + "\n", + "q_AC = 0\n", + "q_BC = q\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", + "\n", + "V_AC = -sym.integrate(q_AC, x) + C_1\n", + "M_AC = sym.integrate(V_AC, x) + C_2\n", + "kappa_AC = M_AC/EI\n", + "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", + "w_AC = -sym.integrate(phi_AC, x) + C_4\n", + "\n", + "V_BC = -sym.integrate(q_BC, x) + C_5\n", + "M_BC = sym.integrate(V_BC, x) + C_6\n", + "kappa_BC = M_BC/EI\n", + "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", + "w_BC = -sym.integrate(phi_BC, x) + C_8\n", + "\n", + "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", + "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", + "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", + "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", + "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", + "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", + "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", + "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", + "\n", + "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", + "for k, v in sol.items():\n", + " print(f\"{k} = {v} ≈ {v.evalf()}\")" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "::::::{prf:example-end}\n", + "::::::" + ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "## Meer voorbeelden\n", + "Het voorbeeld in hoofdstuk 2.7.3 van het boek Mechanica, spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013` richt zich op extensie. Voorbeeld 3b in hoofdstuk 8.2 is een buigingsprobleem.\n", + "\n", + "## Instructies in collegevorm\n", + "\n", + "Dit onderwerp is [in les 3](https://collegerama.tudelft.nl/Mediasite/Channel/public-ceg-ctb2210/watch/757766716fcb45718fca678dadaa396d1d?sortBy=most-recent) gepresenteerd in collegevorm van 0:40:00 tot 0:48:30\n", + "\n", + "## Opgaves\n", + "\n", + "- Opgaves 2.48 en 2.50 in hoofdstuk 2.9 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter2/).\n", + "- Opgaves 8.7, 8.10, 8.11 in hoofdstuk 8.6 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter8/)." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 2 +} diff --git a/book/lesoefeningen.ipynb b/book/lesoefeningen.ipynb new file mode 100644 index 0000000..b6e6d48 --- /dev/null +++ b/book/lesoefeningen.ipynb @@ -0,0 +1,316 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "44f13f99", + "metadata": {}, + "source": [ + "````{margin}\n", + "```{attributiongrey} Bronvermelding\n", + ":class: attribution\n", + "\n", + "Deze oefening is aangepast van https://oit.tudelft.nl/CT1000/2024/week_3/session_1/intro.html\n", + "\n", + "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_3/session_1/intro_data/Tekening1.vsdx\n", + "\n", + "```\n", + "```` \n", + "\n", + "# Begeleide oefening 1_oud\n", + "\n", + "Gegeven is de volgende constructie:\n", + "\n", + "```{figure} lesoefeningen_data/figure2.svg\n", + "---\n", + "align: center\n", + "---\n", + "Constructie, $EI = 6400 \\ \\rm{kNm^2}$\n", + "```\n", + "\n", + ":::::{exercise}\n", + ":label: ODE_3_1\n", + ":nonumber: true\n", + "\n", + "Bepaal de segmenten en teken de vrijlichaamsschema's.\n", + "\n", + "```{h5p} https://tudelft.h5p.com/content/1292630673009985967/embed\n", + "```\n", + "\n", + ":::::\n", + "\n", + "% solution_start\n", + "\n", + "::::{admonition} Solution\n", + ":class: solution, dropdown\n", + "\n", + "$q_z = -12 \\cdot x + 0$\n", + "\n", + "::::\n", + "\n", + "% solution_end\n", + "\n", + ":::::{exercise}\n", + ":label: ODE_3_2\n", + ":nonumber: true\n", + "\n", + "Bepaal de relaties van de krachtsgrootheden en verplaatsingen.\n", + "\n", + "```{h5p} https://tudelft.h5p.com/content/1292630677998825487/embed\n", + "```\n", + "\n", + ":::::\n", + "\n", + "% solution_start\n", + "\n", + "::::{admonition} Solution\n", + ":class: solution, dropdown\n", + "\n", + "- $ w \\left( 0\\right) = 0 $\n", + "- $ M \\left( 0\\right) = 0$\n", + "- $ w \\left( 8\\right) = 0$\n", + "- $ \\varphi \\left( 8 \\right) = 0$\n", + "\n", + "::::\n", + "\n", + "% solution_end\n", + "\n", + ":::::{exercise-start}\n", + ":label: ODE_3_3\n", + ":nonumber: true\n", + "\n", + "Los de integratieconstanten op. Je kan gebruik maken van de volgende code cellen. (Klik op {fa}`rocket` --> {guilabel}`Live Code` om live python code te activeren op deze pagina.)\n", + "\n", + "```{h5p} https://tudelft.h5p.com/content/1292630704310133517/embed\n", + "```\n", + "\n", + ":::::\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "91dc34be", + "metadata": {}, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "A = np.array([\n", + " [ , , , ],\n", + " [ , , , ],\n", + " [ , , , ],\n", + " [ , , , ],\n", + "])\n", + "\n", + "b = np.array([\n", + " ,\n", + " ,\n", + " ,\n", + " ,\n", + "])\n" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "ce6f1792", + "metadata": {}, + "outputs": [], + "source": [ + "np.linalg.solve(A,b)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "9e466305", + "metadata": {}, + "outputs": [], + "source": [ + "import sympy as sym\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = \n", + "EI = 6400\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "\n", + "V = -sym.integrate(q, x) + C_1\n", + "M = sym.integrate(V, x) + C_2\n", + "kappa = M/EI\n", + "phi = sym.integrate(kappa, x) + C_3\n", + "w = -sym.integrate(phi, x) + C_4\n", + "\n", + "eq1 = sym.Eq(w.subs(x,0), )\n", + "eq2 = sym.Eq( .subs(x,0), )\n", + "eq3 = sym.Eq(w.subs(x,8), )\n", + "eq4 = sym.Eq( .subs(x,8),0)\n", + "\n", + "sol = sym.solve([eq1,eq2,eq3,eq4],(C_1,C_2,C_3,C_4))\n", + "for k, v in sol.items():\n", + " print(f\"{k} = {v} ≈ {v.evalf()}\")" + ] + }, + { + "cell_type": "markdown", + "id": "94270e4f", + "metadata": {}, + "source": [ + ":::::{exercise-end}\n", + ":::::\n", + "\n", + "% solution_start\n", + "\n", + "::::{solution-start} ODE_3_3\n", + ":class: dropdown\n", + "::::\n", + "\n", + "$ w = - \\cfrac{x^{5}}{10} + \\cfrac{64 x^{3}}{5} - \\cfrac{2048 x}{5} = - 0.1 x^{5} + 12.8 x^{3} - 409.6 x $\n", + "% solution_end" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "f46e4efd", + "metadata": { + "tags": [ + "solution" + ] + }, + "outputs": [], + "source": [ + "import numpy as np\n", + "\n", + "A = np.array([\n", + " [ 0 , 0 , 0,1],\n", + " [ 0 , 1 , 0,0],\n", + " [-1/75 ,-1/200,-8,1],\n", + " [ 1/200, 1/800, 1,0],\n", + "])\n", + "\n", + "b = np.array([\n", + " 0,\n", + " 0,\n", + " 64/125,\n", + " -8/25,\n", + "])\n" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "300cd851", + "metadata": { + "tags": [ + "solution" + ] + }, + "outputs": [ + { + "data": { + "text/plain": [ + "array([-7.68e+01, 0.00e+00, 6.40e-02, 0.00e+00])" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "np.linalg.solve(A,b)" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "96516034", + "metadata": { + "tags": [ + "solution" + ] + }, + "outputs": [ + { + "data": { + "image/png": "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", + "text/latex": [ + "$\\displaystyle - 0.1 x^{5} + 12.8 x^{3} - 409.6 x$" + ], + "text/plain": [ + " 5 3 \n", + "- 0.1⋅x + 12.8⋅x - 409.6⋅x" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import sympy as sym\n", + "sym.init_printing()\n", + "\n", + "x = sym.symbols('x')\n", + "\n", + "q = -12 * x\n", + "EI = 6400\n", + "\n", + "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", + "\n", + "V = -sym.integrate(q, x) + C_1\n", + "M = sym.integrate(V, x) + C_2\n", + "kappa = M/EI\n", + "phi = sym.integrate(kappa, x) + C_3\n", + "w = -sym.integrate(phi, x) + C_4\n", + "\n", + "eq1 = sym.Eq(w.subs(x,0),0)\n", + "eq2 = sym.Eq(M.subs(x,0),0)\n", + "eq3 = sym.Eq(w.subs(x,8),0)\n", + "eq4 = sym.Eq(phi.subs(x,8),0)\n", + "\n", + "sol = sym.solve([eq1,eq2,eq3,eq4],(C_1,C_2,C_3,C_4))\n", + "\n", + "display((w.subs(sol)*EI).evalf())\n", + "\n", + "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4), (C_1,C_2,C_3,C_4))" + ] + }, + { + "cell_type": "markdown", + "id": "b3a4c9d4", + "metadata": {}, + "source": [ + "% solution_start\n", + "\n", + "::::{solution-end}\n", + "::::\n", + "\n", + "% solution_end" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "base", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.12.11" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From 2a78126224a6db81b2bf734253319c9fcd93fa1f Mon Sep 17 00:00:00 2001 From: Tom van Woudenberg Date: Sat, 3 Jan 2026 15:42:01 +0100 Subject: [PATCH 12/27] change labels --- book/instructie_2.ipynb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/book/instructie_2.ipynb b/book/instructie_2.ipynb index 4b1a93d..0382117 100644 --- a/book/instructie_2.ipynb +++ b/book/instructie_2.ipynb @@ -26,7 +26,7 @@ "\n", "::::::{prf:algorithm} Differentiaalvergelijkingen toepassen\n", ":nonumber: true\n", - ":label: differentiaalvergelijkingen_algoritme\n", + ":label: differentiaalvergelijkingen_algoritme_2\n", "\n", "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", @@ -39,7 +39,7 @@ "\n", "::::::{prf:example}\n", ":nonumber: true\n", - ":label: df_example_0\n", + ":label: df_example_0_2\n", "\n", "Gegeven een constructie zoals hieronder weergegeven:\n", "\n", @@ -55,7 +55,7 @@ "\n", "::::::{prf:example}\n", ":nonumber: true\n", - ":label: df_example_1\n", + ":label: df_example_1_2\n", "\n", "In dit voorbeeld moeten we element AB in twee delen opsplitsen omdat de verdeelde belasting een discontinuïteit veroorzaakt.\n", "\n", @@ -85,7 +85,7 @@ "\n", "::::::{prf:example}\n", ":nonumber: true\n", - ":label: df_example_3\n", + ":label: df_example_3_2\n", "\n", "In dit voorbeeld is er enkel invloed door buiging, dus hebben we aan deze differentiaalvergelijking genoeg: $ -EI \\cfrac{{d}^4w}{{d}x^4} = -q_z$. De differentiaalvergelijkingen voor rek $ EA \\cfrac{{d}^2u}{{d}x^2} = -q_x$ en torsie $ GI \\cfrac{{d}^2\\varphi_x}{{d}x^2} = -q_T$ hebben we in dit geval niet nodig.\n", "\n", @@ -164,7 +164,7 @@ "\n", "::::::{prf:example}\n", ":nonumber: true\n", - ":label: df_example_4\n", + ":label: df_example_4_2\n", "\n", "Bij C kan een randvoorwaarde worden geïdentificeerd uit de consistentie van verplaatsingen: zowel de neerwaartse als rotatieverplaatsing moeten hetzelfde zijn als de neerwaartse en rotatieverplaatsing aan de linkerkant van element BE:\n", "\n", @@ -210,7 +210,7 @@ "\n", "::::::{prf:example-start}\n", ":nonumber: true\n", - ":label: df_example_5\n", + ":label: df_example_5_2\n", "\n", "::::::\n", "\n", From 29fe49b63c1c2bb6433e904cbff105f11e2c46e4 Mon Sep 17 00:00:00 2001 From: Tom van Woudenberg Date: Sat, 3 Jan 2026 15:46:57 +0100 Subject: [PATCH 13/27] zonder override --- book/_config.yml | 2 -- 1 file changed, 2 deletions(-) diff --git a/book/_config.yml b/book/_config.yml index ab31891..2b6da68 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -21,8 +21,6 @@ sphinx: - sphinx_apa_references - sphinx_tudelft_theme config: - sphinx_gated_directives: - override_existing: ['exercise'] language: en html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js From 3f6d1f1d1b37811d7786c758c70db908825611f2 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 11:04:17 +0100 Subject: [PATCH 14/27] Update _config.yml --- book/_config.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/book/_config.yml b/book/_config.yml index 2b6da68..ab31891 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -21,6 +21,8 @@ sphinx: - sphinx_apa_references - sphinx_tudelft_theme config: + sphinx_gated_directives: + override_existing: ['exercise'] language: en html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js From c6e968a9473497d7a9eb3084bc9770dafd8532f9 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 11:06:32 +0100 Subject: [PATCH 15/27] Update requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 1c93c95..3c790d4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ teachbooks == 0.2.3 sphinx-inline-tabs -git+https://github.com/TeachBooks/TeachBooks-Favourites +git+https://github.com/TeachBooks/TeachBooks-Favourites #bump codex teachbooks-sphinx-grasple From 688c16fe71c2ba208984d6573f25365205b9a8b3 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 11:23:45 +0100 Subject: [PATCH 16/27] Remove custom code alignment CSS and update requirements Deleted the code-align.css file that enforced left alignment for docutils code cells. Updated the requirements.txt comment for TeachBooks-Favourites to reference 'patches' instead of 'codex'. --- book/_static/code-align.css | 6 ------ requirements.txt | 2 +- 2 files changed, 1 insertion(+), 7 deletions(-) delete mode 100644 book/_static/code-align.css diff --git a/book/_static/code-align.css b/book/_static/code-align.css deleted file mode 100644 index b9b324a..0000000 --- a/book/_static/code-align.css +++ /dev/null @@ -1,6 +0,0 @@ -/* this to enforce left alignment for docutils content (i.e. code cells) */ -/* assumption is that all code is left aligned, so we enforce that */ -/* if not desired, raise an issue with a use case */ -div.cell.docutils.container { - text-align: left !important; -} \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 3c790d4..5e3d9b4 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ teachbooks == 0.2.3 sphinx-inline-tabs -git+https://github.com/TeachBooks/TeachBooks-Favourites #bump codex +git+https://github.com/TeachBooks/TeachBooks-Favourites #bump patches teachbooks-sphinx-grasple From abec15b938e181171dcdc057245fb63dded7ae7d Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 11:57:49 +0100 Subject: [PATCH 17/27] Switch sphinx_gated_directives to external package Removed the local sphinx_gated_directives extension and updated configuration to use the external package. Added Sphinx-Gated-Directives as a dependency in requirements.txt and adjusted _config.yml to load it as an extra extension. --- book/_config.yml | 3 +- book/_ext/sphinx_gated_directives/__init__.py | 545 ------------------ requirements.txt | 4 +- 3 files changed, 4 insertions(+), 548 deletions(-) delete mode 100644 book/_ext/sphinx_gated_directives/__init__.py diff --git a/book/_config.yml b/book/_config.yml index ab31891..02e26d1 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -11,8 +11,6 @@ html: baseurl : "https://teachbooks.io/manual/" sphinx: - local_extensions: - sphinx_gated_directives: _ext extra_extensions: - sphinx_inline_tabs - sphinx.ext.imgconverter @@ -20,6 +18,7 @@ sphinx: - teachbooks_sphinx_grasple - sphinx_apa_references - sphinx_tudelft_theme + - sphinx_gated_directives config: sphinx_gated_directives: override_existing: ['exercise'] diff --git a/book/_ext/sphinx_gated_directives/__init__.py b/book/_ext/sphinx_gated_directives/__init__.py deleted file mode 100644 index 5aef914..0000000 --- a/book/_ext/sphinx_gated_directives/__init__.py +++ /dev/null @@ -1,545 +0,0 @@ -""" -~~~~~~~~~~~~~~~~~~~~ - -sphinx_gated_directives - -~~~~~~~~~~~~~~~~~~~~ - -This package is an extension for Sphinx that creates a '-start' companion -and a '-end' companion for every registered class-based directive. - -These new directives can be used to "gate" content in the documentation, -similar to how the gated directives of sphinx-exercise work. - -This extension is based on the extension executablebooks/sphinx-exercise: -https://github.com/executablebooks/sphinx-exercise - -Original project licensed under MIT © Executable Books Developers. -See the repository LICENSE for details. - -Our implementation reuses certain ideas and code snippets (e.g., Docutils -compatibility helpers) to fit the needs of this extension. - -""" - -from __future__ import annotations -from docutils.nodes import Element -from typing import Iterator -import copy -import importlib -import inspect -from typing import Dict, Iterable, Optional -from sphinx.transforms import SphinxTransform -from sphinx.application import Sphinx -from sphinx.environment import BuildEnvironment - -from sphinx.util import logging -logger = logging.getLogger(__name__) - -from docutils import nodes -from docutils.parsers.rst import Directive -from docutils.parsers.rst import directives as du_directives - -from dataclasses import dataclass, field - -SUFFIX_START = "start" -SUFFIX_END = "end" -SUFFIX_SEPARATOR = "-" - -def _is_class_directive(obj) -> bool: - return inspect.isclass(obj) and issubclass(obj, Directive) - -def _get_unified_registry(app=None) -> Dict[str, object]: - # 1) Already-loaded directives (includes Sphinx & extensions registered via add_directive) - unified: Dict[str, object] = dict(getattr(du_directives, "_directives", {})) - - # 2) Ensure Docutils built-ins are present by importing from _directive_registry - reg = getattr(du_directives, "_directive_registry", {}) - for name, (modname, clsname) in reg.items(): - if name in unified: - continue - try: - mod = importlib.import_module(f"docutils.parsers.rst.directives.{modname}") - obj = getattr(mod, clsname, None) - if obj is not None: - unified[name] = obj - except Exception: - # Be defensive: if an import fails, just skip that name - continue - - # 3) Add Sphinx's registry if app is provided - if app is not None: - # Sphinx stores directives in app.registry - # Try different ways to access the directives mapping - if hasattr(app.registry, 'directives'): - # Newer Sphinx versions may have a directives attribute - try: - unified.update(app.registry.directives) - except (AttributeError, TypeError): - pass - - # Also check for domains which contain directives (e.g., prf:theorem from sphinx-proof) - if hasattr(app.registry, 'domains'): - for domain_name, domain_cls in app.registry.domains.items(): - try: - # Instantiate domain or use class attributes - if hasattr(domain_cls, 'directives'): - domain_directives = domain_cls.directives - for dir_name, dir_impl in domain_directives.items(): - # Fully qualified name: domain:directive - full_name = f"{domain_name}:{dir_name}" - if full_name not in unified: - unified[full_name] = dir_impl - except Exception: - continue - - return unified - -def _copy_option_spec(option_spec): - return copy.copy(option_spec) if isinstance(option_spec, dict) else option_spec - -def make_start_class(orig_name: str, base_cls: type[Directive]) -> type[Directive]: - - attrs = {} - for attr in ( - "required_arguments", - "optional_arguments", - "final_argument_whitespace", - "has_content", - "option_spec", - ): - if hasattr(base_cls, attr): - val = getattr(base_cls, attr) - if attr == "option_spec": - attrs[attr] = _copy_option_spec(val) or {} - else: - attrs[attr] = val - - def run(self: Directive): - current_name = self.name - self.name = orig_name # temporarily set to original for base run() - children = base_cls.run(self) - self.name = current_name # restore - if not isinstance(children, list): - if isinstance(children, Iterable): - children = list(children) - else: - children = [children] - # create a start_node and add all result nodes as its children - start_node_instance = start_node() - start_node_instance += children - result = [start_node_instance] - - # Get environment from state.document.settings - env = getattr(self.state.document.settings, "env", None) - if env is not None: - docname = env.docname - # 1) Check whether super registry has been created, if not, create it - if not hasattr(env, "sphinx_gated_directives_registry"): - env.sphinx_gated_directives_registry = {} - # 2) Register current usage in the super registry - registry = env.sphinx_gated_directives_registry - if docname not in registry: - registry[docname] = { - "start": [], - "end": [], - "sequence": [], - "msg": [], - "type": [], - } - registry[docname]["start"].append(self.lineno) - registry[docname]["sequence"].append("S") - registry[docname]["msg"].append( - f"{self.name} at line: {self.lineno}" - ) - registry[docname]["type"].append(orig_name) - - return result - - attrs["run"] = run - new_cls_name = f"{base_cls.__name__}_Start_For_{orig_name.replace(':', '_')}" - return type(new_cls_name, (base_cls,), attrs) - -def make_end_class(orig_name: str, base_cls: type[Directive]) -> type[Directive]: - - attrs = {} - for attr in ( - "required_arguments", - "optional_arguments", - "final_argument_whitespace", - "has_content", - "option_spec", - ): - if hasattr(base_cls, attr): - val = getattr(base_cls, attr) - if attr == "option_spec": - attrs[attr] = _copy_option_spec(val) or {} - else: - attrs[attr] = val - - def run(self: Directive): - - # Get environment from state.document.settings - env = getattr(self.state.document.settings, "env", None) - if env is not None: - docname = env.docname - # 1) Check whether super registry has been created, if not, create it - if not hasattr(env, "sphinx_gated_directives_registry"): - env.sphinx_gated_directives_registry = {} - # 2) Register current usage in the super registry - registry = env.sphinx_gated_directives_registry - if docname not in registry: - registry[docname] = { - "start": [], - "end": [], - "sequence": [], - "msg": [], - "type": [], - } - registry[docname]["end"].append(self.lineno) - registry[docname]["sequence"].append("E") - registry[docname]["msg"].append( - f"{self.name} at line: {self.lineno}" - ) - registry[docname]["type"].append(orig_name) - - return [end_node()] - - attrs["run"] = run - new_cls_name = f"{base_cls.__name__}_End_For_{orig_name.replace(':', '_')}" - return type(new_cls_name, (base_cls,), attrs) - -def _should_skip_name(orig_name: str, cfg: dict) -> bool: - suffix_separator = cfg["suffix_separator"] - suffix_start = cfg["suffix_start"] - suffix_end = cfg["suffix_end"] - if isinstance(cfg["override_existing"],bool): - if cfg["override_existing"] is True: - return False - else: - if orig_name.endswith(f"{suffix_separator}{suffix_start}"): - return True - else: - return orig_name.endswith(f"{suffix_separator}{suffix_end}") - elif isinstance(cfg["override_existing"],list): - if orig_name in cfg["override_existing"]: - return False - else: - if orig_name.endswith(f"{suffix_separator}{suffix_start}"): - return True - else: - return orig_name.endswith(f"{suffix_separator}{suffix_end}") - -def _register_new_directives(app, env, docnames): - - # get options and add missing default values - cfg = app.config.sphinx_gated_directives - if "override_existing" not in cfg: - cfg["override_existing"] = False - elif isinstance(cfg["override_existing"], str): - cfg["override_existing"] = [cfg["override_existing"]] - if "suffix_start" not in cfg: - cfg["suffix_start"] = SUFFIX_START - if "suffix_end" not in cfg: - cfg["suffix_end"] = SUFFIX_END - if "suffix_separator" not in cfg: - cfg["suffix_separator"] = SUFFIX_SEPARATOR - - unified = _get_unified_registry(app) - snapshot_names = set(unified.keys()) - added = 0 - - suffix_separator = cfg["suffix_separator"] - suffix_start = cfg["suffix_start"] - suffix_end = cfg["suffix_end"] - for orig_name, obj in sorted(unified.items()): - - new_name = f"{orig_name}{suffix_separator}{suffix_start}" - # add start and end directives if not already present, or if override is requested explicitly - if new_name in snapshot_names: - if isinstance(cfg["override_existing"], bool): - if cfg["override_existing"] is False: - continue - else: - if orig_name not in cfg["override_existing"]: - continue - end_name = f"{orig_name}{suffix_separator}{suffix_end}" - if end_name in snapshot_names: - if isinstance(cfg["override_existing"], bool): - if cfg["override_existing"] is False: - continue - else: - if orig_name not in cfg["override_existing"]: - continue - - # if the original name is itself a start or end directive, skip - if _should_skip_name(orig_name,cfg): - continue - - try: - if _is_class_directive(obj): - start_cls = make_start_class(orig_name, obj) # new class based on original so all properties are inherited - app.add_directive(new_name, start_cls,override=True) - end_cls = make_end_class(orig_name, Directive) # because it does not generate anything, most simple directive possible - app.add_directive(end_name, end_cls,override=True) - added += 1 - else: - logger.debug(f"[sphinx-gated-directives] '{orig_name}' is not a class directive; skipping.") - except Exception as e: - logger.warning(f"[sphinx-gated-directives] failed to register new classes for directive '{orig_name}':\n{e}") - -def setup(app): - # Register a config value to set options for the extension - app.add_config_value("sphinx_gated_directives", {}, "env") - - # Register a function to check if config value structure is correct - app.connect("config-inited", check_config_values) - - # register function to purge registries at start of build per document - app.connect("env-purge-doc", purge_registries) - - # At the latest possible moment, register new directives - app.connect("env-before-read-docs", _register_new_directives,priority=10000000000000000000000000000) - - # Register nodes - # During transformation phase, these nodes will be removed - app.add_node(start_node) - app.add_node(end_node) - - # Register a transform to check validity of start-end pairs - app.add_transform(CheckGatedDirectivesTransform) - - # Register a transform to merge start-end pairs into gated content - app.add_transform(MergeGatedDirectivesTransform) - - return { - "parallel_read_safe": True, - } - -class start_node(nodes.Admonition, nodes.Element): - pass - -class end_node(nodes.Admonition, nodes.Element): - pass - -def purge_registries(app: Sphinx, env: BuildEnvironment, docname: str) -> None: - - if hasattr(env, "sphinx_gated_directives_registry"): - registry = env.sphinx_gated_directives_registry - if docname in registry: - del registry[docname] - -# Transform to check validity of start-end pairs -class CheckGatedDirectivesTransform(SphinxTransform): - default_priority = 1 - - def apply(self, **kwargs): - env = self.env - if not hasattr(env, "sphinx_gated_directives_registry"): - return - - registry = env.sphinx_gated_directives_registry - error = False - docname = self.env.docname - if docname in registry: - start = registry[docname]["start"] - end = registry[docname]["end"] - structure = "\n ".join(registry[docname]["msg"]) - sequence = "".join(registry[docname]["sequence"]) - validate_SE_result = validate_SE(sequence) - if len(start) > len(end): - msg = f"[sphinx-gated-directives] The document {docname} contains more start directives than end directives:\n {structure}\nPlease ensure each start directive has a corresponding end directive." - logger.error(msg) - error = True - elif len(end) > len(start): - msg = f"[sphinx-gated-directives] The document {docname} contains more end directives than start directives:\n {structure}\nPlease ensure each end directive has a corresponding start directive." - logger.error(msg) - error = True - # at this point, len(start) == len(end) - # now check for correct nesting - elif not validate_SE_result.is_valid: - msg = f"[sphinx-gated-directives] The document {docname} contains incorrectly nested start and end directives:\n {structure}\nThis is not allowed. Please correct the nesting." - logger.error(msg) - error = True - else: - # At this point, every start is matched with an end - # Now check that types match in order. - types = registry[docname]["type"] - start_type = [types[k] for k in validate_SE_result.pairs.keys()] - end_type = [types[v] for v in validate_SE_result.pairs.values()] - for i in range(len(start_type)): - if start_type[i] != end_type[i]: - msg = f"[sphinx-gated-directives] The document {docname} contains mismatched start and end directives at lines {start[i]} and {end[i]}:\n {structure}\nPlease ensure that start and end directives match in type." - logger.error(msg) - error = True - break - - if error: - raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") - -class MergeGatedDirectivesTransform(SphinxTransform): - default_priority = 10 - - def apply(self, **kwargs): - env = self.env - if not hasattr(env, "sphinx_gated_directives_registry"): - return - registry = env.sphinx_gated_directives_registry - docname = self.env.docname - if docname not in registry: - return - - start_nodes = findall(self.document, start_node) - for start_n in start_nodes: - parent = start_n.parent - found_start = False - end_n = None - skip_end = 0 - for child in parent.children: - if child is start_n: - found_start = True - elif found_start and isinstance(child, end_node) and skip_end==0: - end_n = child - break - elif found_start and isinstance(child, end_node): - skip_end -= 1 - elif found_start and isinstance(child, start_node): - skip_end += 1 - if end_n is None: - logger.error(f"[sphinx-gated-directives] Could not find matching end directive for start directive at line {start_n.line} in document {docname}. Skipping merging for this pair.") - raise Exception(f"[sphinx-gated-directives] An error has occurred when parsing gated directives in {docname}.\nPlease check warning messages above.") - - start_index = parent.children.index(start_n) - end_index = parent.children.index(end_n) - between_nodes = parent.children[start_index + 1:end_index] - - new_nodes = start_n.children - if between_nodes: - has_section = False - has_caption = False - si = -1 - content = new_nodes[-1] - for sn in content.children: - si += 1 - if isinstance(sn, nodes.section): - has_section = True - section_index = si - section_node = sn - break - elif isinstance(sn, nodes.caption): - has_caption = True - caption_index = si - caption_node = sn - break - if has_section: - content[section_index] += between_nodes - elif has_caption: - n = len(content.children) - pos = n - 1 - for i, bn in enumerate(between_nodes): - content.insert(pos + i, bn) - else: - for bn in between_nodes: - content += bn - - start_pos = parent.children.index(start_n) - parent.remove(end_n) - parent.remove(start_n) - for bn in between_nodes: - parent.remove(bn) - for i, nn in enumerate(new_nodes): - parent.insert(start_pos + i, nn) - - -# SPDX-License-Identifier: MIT -# Adapted from: executablebooks/sphinx-exercise, file: sphinx_exercise/_compat.py -# Source: https://github.com/executablebooks/sphinx-exercise/blob/main/sphinx_exercise/_compat.py -# License: MIT — © Executable Books Developers (see repository LICENSE). -def findall(node: Element, *args, **kwargs) -> Iterator[Element]: - # findall replaces traverse in docutils v0.18 - # note a difference is that findall is an iterator - impl = getattr(node, "findall", node.traverse) - return iter(impl(*args, **kwargs)) - -@dataclass -class SEValidationResult: - is_valid: bool - pairs: Dict[int, int] = field(default_factory=dict) # {S_index: E_index} - error_index: Optional[int] = None # index of first error, if any - error_message: Optional[str] = None # description of the error, if any - -def validate_SE(s: str) -> SEValidationResult: - stack = [] - pairs: Dict[int, int] = {} - - for i, ch in enumerate(s): - if ch == 'S': - stack.append(i) - elif ch == 'E': - if not stack: - return SEValidationResult( - is_valid=False, - pairs=pairs, - error_index=i, - error_message="Encountered 'E' before a matching 'S'." - ) - s_index = stack.pop() - pairs[s_index] = i - else: - return SEValidationResult( - is_valid=False, - pairs=pairs, - error_index=i, - error_message=f"Invalid character: {ch!r}. Only 'S' and 'E' are allowed." - ) - - if stack: - return SEValidationResult( - is_valid=False, - pairs=pairs, - error_index=len(s), - error_message=f"{len(stack)} unmatched 'S' remaining at end." - ) - - return SEValidationResult(is_valid=True, pairs=pairs) - -def check_config_values(app: Sphinx, config) -> None: - cfg = config.sphinx_gated_directives - if not isinstance(cfg, dict): - logger.error("[sphinx-gated-directives] The configuration value 'sphinx_gated_directives' should be a dictionary.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives'. Expected a dictionary.") - return - # Implemented options: - # - override_existing: bool (default: False), string or list of strings - override_existing = cfg.get("override_existing", False) - if not (isinstance(override_existing, bool) or isinstance(override_existing, str) or (isinstance(override_existing, list) and all(isinstance(x, str) for x in override_existing))): - logger.error("[sphinx-gated-directives] The configuration option 'override_existing' should be a boolean, a string, or a list of strings.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.override_existing'. Expected a boolean, string, or list of strings.") - # - suffix_start: string (default: 'start'), only a-z allowed, or empty string - suffix_start = cfg.get("suffix_start", SUFFIX_START) - if not isinstance(suffix_start, str): - logger.error("[sphinx-gated-directives] The configuration option 'suffix_start' should be a string.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_start'. Expected a string.") - if suffix_start != "" and (not suffix_start.islower() or not suffix_start.isalpha()): - logger.error("[sphinx-gated-directives] The configuration option 'suffix_start' should contain only lowercase letters (a-z).") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_start'. Expected only lowercase letters (a-z).") - # - suffix_end: string (default: 'end'), only a-z allowed, or empty string - suffix_end = cfg.get("suffix_end", SUFFIX_END) - if not isinstance(suffix_end, str): - logger.error("[sphinx-gated-directives] The configuration option 'suffix_end' should be a string.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_end'. Expected a string.") - if suffix_end != "" and (not suffix_end.islower() or not suffix_end.isalpha()): - logger.error("[sphinx-gated-directives] The configuration option 'suffix_end' should contain only lowercase letters (a-z).") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_end'. Expected only lowercase letters (a-z).") - # - suffix_separator: string (default: '-'), single character, no space, no underscore, no colon, or empty string - suffix_separator = cfg.get("suffix_separator", SUFFIX_SEPARATOR) - if not isinstance(suffix_separator, str): - logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should be a string.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected a string.") - if len(suffix_separator) > 1: - logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should be a single character or an empty string.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected a single character or an empty string.") - if suffix_separator in [' ', '_',':']: - logger.error("[sphinx-gated-directives] The configuration option 'suffix_separator' should not be a space, nor an underscore, nor a colon.") - raise ValueError("Invalid configuration for 'sphinx_gated_directives.suffix_separator'. Expected something other than a space, an underscore, or a colon.") - \ No newline at end of file diff --git a/requirements.txt b/requirements.txt index 5e3d9b4..770b879 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,6 @@ myst_nb numpy matplotlib -sphinx-tudelft-theme == 1.2.1 \ No newline at end of file +sphinx-tudelft-theme == 1.2.1 + +git+https://github.com/TeachBooks/Sphinx-Gated-Directives \ No newline at end of file From dda97322466371ce97f4163fabe6c1a329966ae4 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 12:26:58 +0100 Subject: [PATCH 18/27] Update comment for TeachBooks-Favourites dependency Changed the comment for the TeachBooks-Favourites git dependency from 'bump patches' to 'bump codex' to clarify the purpose of the dependency update. --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 770b879..1e0384a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ teachbooks == 0.2.3 sphinx-inline-tabs -git+https://github.com/TeachBooks/TeachBooks-Favourites #bump patches +git+https://github.com/TeachBooks/TeachBooks-Favourites #bump codex teachbooks-sphinx-grasple From 804bae0deec3860af0036d73028f8dfb21d41389 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 13:05:52 +0100 Subject: [PATCH 19/27] Add note inside axiom-end directive in intro Inserted a line of text within the prf:axiom-end directive in intro.md to clarify that this text will not be rendered. --- book/intro.md | 1 + 1 file changed, 1 insertion(+) diff --git a/book/intro.md b/book/intro.md index 4df190b..fae52ac 100644 --- a/book/intro.md +++ b/book/intro.md @@ -24,6 +24,7 @@ This is a child axiom. This text is inside the child directive. This is text after the child-start, before the child-end, inside the parent theorem. :::{prf:axiom-end} +Text inside the end. This text will not be rendered. ::: Finally some text after the entire child, inside the theorem start. From ab38930bb3b16e4844ed98bd9fe669916aa14ab5 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 16:20:16 +0100 Subject: [PATCH 20/27] Update TOC and requirements for TeachBooks changes Added an external manual link to the table of contents. Updated requirements to use TeachBooks from the 'issue-127' branch and adjusted comments for clarity. --- book/_toc.yml | 1 + requirements.txt | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/book/_toc.yml b/book/_toc.yml index 561237f..6c408a7 100644 --- a/book/_toc.yml +++ b/book/_toc.yml @@ -4,6 +4,7 @@ root: intro parts: - caption: test sphinx_gated_directives chapters: + - external: https://github.com/TeachBooks/Sphinx-Gated-Directives/blob/main/MANUAL.md - file: instructie.ipynb - file: instructie_2.ipynb - file: lesoefeningen.ipynb diff --git a/requirements.txt b/requirements.txt index 1e0384a..946f15a 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,8 @@ -teachbooks == 0.2.3 +# teachbooks == 0.2.3 +git+https://github.com/TeachBooks/TeachBooks@issue-127 sphinx-inline-tabs -git+https://github.com/TeachBooks/TeachBooks-Favourites #bump codex +git+https://github.com/TeachBooks/TeachBooks-Favourites teachbooks-sphinx-grasple From a4cb6d70904bffdda6a4db4a9d922223fed1cefc Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 16:51:14 +0100 Subject: [PATCH 21/27] Remove obsolete instruction notebooks and update ToC Deleted instructie.ipynb, instructie_2.ipynb, and lesoefeningen.ipynb from the book. Updated _toc.yml to remove references to these files and moved the Sphinx-Gated-Directives manual link to the correct section. Minor update to intro.md. --- book/_toc.yml | 7 +- book/instructie.ipynb | 1470 -------------------------------------- book/instructie_2.ipynb | 1470 -------------------------------------- book/intro.md | 205 ------ book/lesoefeningen.ipynb | 316 -------- 5 files changed, 1 insertion(+), 3467 deletions(-) delete mode 100644 book/instructie.ipynb delete mode 100644 book/instructie_2.ipynb delete mode 100644 book/lesoefeningen.ipynb diff --git a/book/_toc.yml b/book/_toc.yml index 6c408a7..6441842 100644 --- a/book/_toc.yml +++ b/book/_toc.yml @@ -2,12 +2,6 @@ format: jb-book root: intro parts: - - caption: test sphinx_gated_directives - chapters: - - external: https://github.com/TeachBooks/Sphinx-Gated-Directives/blob/main/MANUAL.md - - file: instructie.ipynb - - file: instructie_2.ipynb - - file: lesoefeningen.ipynb - caption: Your First TeachBook! chapters: - file: intro/book.md @@ -95,6 +89,7 @@ parts: - external: https://github.com/TeachBooks/Sphinx-ref-graph/blob/manual/README.md - external: https://github.com/TeachBooks/Sphinx-GitHub-Alerts/blob/main/README.md - external: https://github.com/TeachBooks/Sphinx-Metadata-Figure/blob/main/MANUAL.ipynb # BUMP + - external: https://github.com/TeachBooks/Sphinx-Gated-Directives/blob/main/MANUAL.md - file: features/visuals.md sections: - external: https://github.com/TeachBooks/Sphinx-Accessibility/blob/manual/README.md diff --git a/book/instructie.ipynb b/book/instructie.ipynb deleted file mode 100644 index e2ec472..0000000 --- a/book/instructie.ipynb +++ /dev/null @@ -1,1470 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "````{margin}\n", - "```{attributiongrey} Attribution\n", - ":class: attribution\n", - "\n", - "Deze pagina is aangepast van https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro.ipynb\n", - "\n", - "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro_data/Tekening1.vsdx\n", - "\n", - "```\n", - "```` " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Instructie_oud\n", - "\n", - "In constructiemechanica 2 heb je geleerd hoe je met differentiaalvergelijkingen voor statisch bepaalde constructies de krachtsverdeling en vervormingen kan vinden. Precies dezelfde aanpak werkt ook voor statisch onbepaalde constructie, met als enige verschil dat we de inwendige krachten niet kunnen vinden zonder ook de vergelijkingen en randvoorwaarden voor verplaatsingen op te stellen. Dat doen we volgens de volgende stappen:\n", - "\n", - "::::::{prf:algorithm} Differentiaalvergelijkingen toepassen\n", - ":nonumber: true\n", - ":label: differentiaalvergelijkingen_algoritme\n", - "\n", - "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", - "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", - "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", - "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", - "\n", - "::::::\n", - "\n", - "Als voorbeeld bepalen we de een simpele statisch onbepaalde constructie:\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_0\n", - "\n", - "Gegeven een constructie zoals hieronder weergegeven:\n", - "\n", - "```{figure} intro_data/structure.svg\n", - "---\n", - "align: center\n", - "---\n", - "Constructie, $EI = 5000 \\ \\rm{kNm^2}$\n", - "```\n", - "::::::\n", - "\n", - "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_1\n", - "\n", - "In dit voorbeeld moeten we element AB in twee delen opsplitsen omdat de verdeelde belasting een discontinuïteit veroorzaakt.\n", - "\n", - "Voor AC leidt dit tot:\n", - "\n", - "```{figure} intro_data/FBD_AC.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram element AC\n", - "```\n", - "\n", - "Voor BC leidt dit tot:\n", - "\n", - "```{figure} intro_data/FBD_BC.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram element BC\n", - "```\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_3\n", - "\n", - "In dit voorbeeld is er enkel invloed door buiging, dus hebben we aan deze differentiaalvergelijking genoeg: $ -EI \\cfrac{{d}^4w}{{d}x^4} = -q_z$. De differentiaalvergelijkingen voor rek $ EA \\cfrac{{d}^2u}{{d}x^2} = -q_x$ en torsie $ GI \\cfrac{{d}^2\\varphi_x}{{d}x^2} = -q_T$ hebben we in dit geval niet nodig.\n", - "\n", - "**Element AC**\n", - "\n", - "Omdat dit element onbelast is, geeft de belastingsvergelijking voor dit element:\n", - "\n", - "$$q_{z,{\\rm{AC}}}=0$$\n", - "\n", - "wat leidt tot de vergelijkingen:\n", - "\n", - "$$ V_{\\rm{AC}} \\left(x\\right) = C_1$$\n", - "\n", - "$$ M_{\\rm{AC}} \\left(x\\right) = C_1 \\cdot x + C_2 $$\n", - "\n", - "$$ \\kappa_{\\rm{AC}} \\left(x\\right) = \\cfrac{C_1 \\cdot x + C_2}{5000}$$\n", - "\n", - "$$ \\varphi_{\\rm{AC}} \\left(x\\right) = \\cfrac{\\cfrac{1}{2}\\cdot C_1 \\cdot x^2 + C_2 \\cdot x}{5000} + C_3$$\n", - "\n", - "$$ w_{\\rm{AC}} \\left(x\\right) = \\cfrac{-\\cfrac{1}{6}\\cdot C_1 \\cdot x^3 - \\cfrac{1}{2} C_2 \\cdot x^2}{5000} - C_3 \\cdot x + C_4$$\n", - "\n", - "Vanwege de oplegging bij A kan een randvoorwaarde worden geformuleerd als:\n", - "\n", - "```{math}\n", - ":label: BC_1\n", - "w_{\\rm{AC}} \\left(0\\right) = 0\n", - "```\n", - "\n", - "Omdat A ook een onbelast uiteinde is, kan een andere randvoorwaarde worden geformuleerd als:\n", - "\n", - "```{math}\n", - ":label: BC_2\n", - "M_{\\rm{AC}} \\left(0\\right) = 0\n", - "```\n", - "\n", - "Als dit uiteinde belast werd kon met een krachten- of momentenevenwicht ook een relatie gevonden worden.\n", - "\n", - "**Element BC**\n", - "\n", - "Omdat dit element belast is, geeft de belastingsvergelijking voor dit element:\n", - "\n", - "$$q_{z,\\rm{BC}}=10$$\n", - "\n", - "wat leidt tot de vergelijkingen:\n", - "\n", - "$$ V_{\\rm{BC}} \\left(x\\right) = -10 \\cdot x + C_5$$\n", - "\n", - "$$ M_{\\rm{BC}} \\left(x\\right) = -5 \\cdot x^2 + C_5 \\cdot x + C_6 $$\n", - "\n", - "$$ \\kappa_{\\rm{BC}} \\left(x\\right) = \\cfrac{-5 \\cdot x^2 + C_5 \\cdot x + C_6}{5000}$$\n", - "\n", - "$$ \\varphi_{\\rm{BC}} \\left(x\\right) = \\cfrac{-\\cfrac{5}{3} \\cdot x^3 + \\cfrac{1}{2}\\cdot C_5 \\cdot x^2 + C_6 \\cdot x}{5000} + C_7$$\n", - "\n", - "$$ w_{\\rm{BC}} \\left(x\\right) = \\cfrac{\\cfrac{5}{12} \\cdot x^4 -\\cfrac{1}{6}\\cdot C_5 \\cdot x^3 - \\cfrac{1}{2} C_6 \\cdot x^2}{5000} - C_7 \\cdot x + C_8$$\n", - "\n", - "Vanwege de inklemming bij B kunnen twee randvoorwaarden direct worden geformuleerd:\n", - "\n", - "```{math}\n", - ":label: BC_3\n", - "\\varphi_{\\rm{BC}} \\left(4\\right) = 0\n", - "```\n", - "\n", - "```{math}\n", - ":label: BC_4\n", - "w_{\\rm{BC}} \\left(4\\right) = 0\n", - "```\n", - "\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_4\n", - "\n", - "Bij C kan een randvoorwaarde worden geïdentificeerd uit de consistentie van verplaatsingen: zowel de neerwaartse als rotatieverplaatsing moeten hetzelfde zijn als de neerwaartse en rotatieverplaatsing aan de linkerkant van element BE:\n", - "\n", - "```{math}\n", - ":label: BC_5\n", - "w_{\\rm{AC}} \\left(4\\right) = w_{\\rm{BC}} \\left(0\\right)\n", - "```\n", - "\n", - "```{math}\n", - ":label: BC_6\n", - "\\varphi_{\\rm{AC}} \\left(4\\right) = \\varphi_{\\rm{BC}} \\left(0 \\right)\n", - "```\n", - "\n", - "Ten slotte moet er ook consistentie zijn met de snedekrachten, zoals getoond in het vrijlichaamdiagram:\n", - "\n", - "```{figure} intro_data/FBD_C.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram knoop C\n", - "```\n", - "\n", - "Verticaal evenwicht geeft:\n", - "\n", - "```{math}\n", - ":label: BC_7\n", - "\\sum {{{\\left. {{F_{\\rm{v}}}} \\right|}^{\\rm{C}}} = 0} \\to -{V_{\\rm{AC}}}\\left(4\\right) + V_{\\rm{BC}}\\left(0\\right) = 0\n", - "```\n", - "\n", - "Momentevenwicht geeft:\n", - "\n", - "```{math}\n", - ":label: BC_8\n", - "\\sum {{{\\left. {{T}} \\right|}_{\\rm{C}}^{\\rm{C}}} = 0} \\to -{M_{{\\rm{AC}}}}\\left(4\\right) + M_{\\rm{BC}}\\left(0\\right) = 0\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", - "\n", - "\n", - "::::::{codex-start}\n", - ":nonumber: true\n", - ":label: df_example_5\n", - "\n", - "::::::\n", - "\n", - "Er zijn 8 randvoorwaarden geformuleerd met 8 integratieconstanten. Deze kunnen nu worden opgelost. \n", - "\n", - "Het invullen van de randvoorwaarden geeft:\n", - "\n", - "$$\n", - "\\begin{align}\n", - "C_{4} = 0 \\\\\n", - "C_{2} = 0 \\\\\n", - "- \\cfrac{4 C_{5}}{1875} - \\cfrac{C_{6}}{625} - 4 C_{7} + C_{8} + \\cfrac{8}{375} = 0 \\\\\n", - "\\cfrac{C_{5}}{625} + \\cfrac{C_{6}}{1250} + C_{7} - \\cfrac{8}{375} = 0 \\\\\n", - "- \\cfrac{4 C_{1}}{1875} - \\cfrac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0 \\\\\n", - "\\cfrac{C_{1}}{625} + \\cfrac{C_{2}}{1250} + C_{3} - C_{7} = 0 \\\\\n", - "4 C_{1} + C_{2} - C_{6} = 0 \\\\\n", - "C_{1} - C_{5} = 0 \\\\\n", - "\\end{align}\n", - "$$\n", - "\n", - "Het oplossen hiervan geeft:\n", - "\n", - "$$\n", - "\\begin{align}\n", - "C_{1} = \\cfrac{35}{8} = 4.375 \\\\\n", - "C_{2} = 0 \\\\\n", - "C_{3} = - \\cfrac{1}{150} \\approx -0.006667 \\\\\n", - "C_{4} = 0 \\\\\n", - "C_{5} = \\cfrac{35}{8} = 4.375\\\\\n", - "C_{6} = \\cfrac{35}{2} = 17.5 \\\\\n", - "C_{7} = \\cfrac{1}{3000} \\approx 0.0003333\\\\\n", - "C_{8} = \\cfrac{13}{750} \\approx 0.01733\n", - "\\end{align}\n", - "$$\n", - "\n", - "Nu is de volledige krachtenverdeling en verplaatsingen van de constructie opgelost. Bijvoorbeeld de momentenverdeling in BC die kan worden uitgezet als:\n", - "\n", - "```{figure} intro_data/Momentenlijn.svg\n", - ":align: center\n", - "\n", - "Momentenlijn BC\n", - "```\n", - "\n", - "De krachtsverdeling en verplaatsingen kunnen no ook \n", - "\n", - "**Oplossen met digitale hulpmiddelen**\n", - "Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python.\n", - "\n", - "\n", - "$$\\left[\\begin{array}{cccccccc}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{array}\\right] \\left[ \\begin{array}{cccccccc} C_1\\\\C_2\\\\C_3\\\\C_4\\\\C_5\\\\C_6\\\\C_7\\\\C_8 \\end{array} \\right] = \\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]$$\n", - "\n", - "[Hier](rref_ti_84) is uitgelegd hoe je dit op een grafische rekenmachine kan oplossen.\n", - "\n", - "Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt.\n", - "\n", - "Beide opties zijn hieronder getoond" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAs9JREFUeJzl102o1FUYx/HPaEh2q3sxCgKtqNRdS7tgQi8g0SZXLgLBCiEsYnITFPTwBLqMUBRq0S2iVhEGtRF6WVTULktLbwTtgsDK6nbpzXFxztTcaeY2M3eGS/SF4YHznJfn9z/nPOeZRqvV0ovM3IwHsBPXYQY/4CRex4sR8UvPwatEZm7E07gbV+EbHEdGxPftfo1u0ZnZQOJxrMOH+BTncb3yETbg/YjYMWkhg5KZNymxXoM3cAbbcAfOYntEnINLeox/AXvxOe6LiJNdk6/HAdw8ofhH5Zgi+NGIONJuzMxn8BgO4iFY0zkqMw8ogr/AbLdgiIjFiDiI/ZOKflgy80blBH6No13uwAL2ZOYUHaIz81rla/yB3RHx03ILRcTi+MJeMXdWeyIiLnQ6qo4PcBlmWXq8m7gUcxFxatJRZmZTSY6D8klEHO/j21rtfB//l8pJ2IK3O0XvqvblIQJZCU0lMQ7KS0om7sV0tef7+NvtM9SdzszLla/QwsdDBPIXmfmEcj2ORsQj/9Y/Im4YZZ0RaVTb4u87fXW1P47y9mbmLPYpT9tq0N7J6T7+Kzv7tY/3QrXrM3NtRPw56GqZOY1X8CCeGmJc0/ju9Nlqt/Txb652nrrTEfGtku7X4fblVs7MNV1Nz+O1iHhnuXE9aCrPyaC/XcvM9W61O7vjy8wrsB2L+Iil2fvZ+juWmfdGxJmuwQ2lvLsfu2vbPqVI2TOw1Mo473REfJWZJ5QM/TCOdLgTU3guIhZYKvowblHq7c/qJPP4HZtwGzbiVcjMrTiEHRHx27gErID9Shl6ODPvUgqsW5UydB5Ptjv2qr3vUZLSNiXB/awU7qfxFt6MiHOZuRdz6Lz/a5UMeQFTEfHrBMT1JTM36f+H47t2v3+IHmKBGWXnO5lTCoFDOB0Ro00+YUYW3YvMfA+nBnmnV5PuTPy/YKw7/V/hImUh8qiSnFTZAAAAAElFTkSuQmCC", - "text/latex": [ - "$\\displaystyle C_{4} = 0$" - ], - "text/plain": [ - "C₄ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAt9JREFUeJzV10+IVXUUB/DPTBGZmWIYBFpSaruWJZmRBSZtkhYGkZBBUAoxSRDS4nACV1HISANt+kPQMoxqIw1tKmqn/TGdCNwFQX+smYbIei1+99ad17vje773kL5wObx7fud3zvf3O+e8cyc6nY5eyMzNeAw7cQPW4GecxNt4PSJ+62l8iZCZ6/E8duFafIdjyIj4qV430U06MyeQeBZX4BN8jnO4UTmEtfgoIraPm0i/yMyblVivwzs4jduwA2ewLSJ+gMt72L+KR3EKD0fEya7NV+AgNo0p/ovFjEL4qYg4Wr/MzJfwNA7jCZhsWmXmQYXw19jaTRgiYjEiDmP/uKIfFJl5k5KBZ/FylzqwgL2ZuZIG6cy8XjmN89gTEb8u5ygiFkcX9tC4p5LHI+KvpqLi8TGuwlaWpvcUrsRrEfHluKPMzCmlOfaLExFxrEV3SyXnWvTfKJmwBbNN0rsr+eYAgQyDKaUx9os3lE7cC6srea5FX79fQ3XTmXm1cgodfNZvFJl5CA8qJ/07PsWhfjIlIjb262cEmKhkh39rel0lfxnwv/dupWveodTVeXyQmWuHj3Mg1De5ukV/TXNdnd4LlVyRmZdFxJ/9eIqI+5q/M3NvtfE2vLuc7Yhr+kwlt7ToN1dyjop0RHyfmWexUbm92TbPmTnZ3SEbWKVkz49t9g1MGV1Nf1jJnd3xZeYq5RIWlfJb0r2PVM9MZj4QEaebu1aT2i7sw54W50dwot58OYyypiPi28w8rnToAzjaUCdW4pWIWGAp6WncqszbX1SbzOEPbMCdWI+3ejnOzBdwF7b3Wx4jxn5lDJ3OzHuVAet2ZQydw3P1wl6z9/14XJlb12FeGdy/wvt4r55hGzYv4hHsiIhT4+F0YWTmBu0fHP+U3H9IX4SjaTzkEhMeBEORzswZ5YZ3Kx8oNeYjYn640MaHyQsvWRZPKh17Vkml+nlmyH3HiqHT+/+IvwHtQ/pzJbnwjAAAAABJRU5ErkJggg==", - "text/latex": [ - "$\\displaystyle C_{2} = 0$" - ], - "text/plain": [ - "C₂ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle - \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0$" - ], - "text/plain": [ - " 4⋅C₅ C₆ 8 \n", - "- ──── - ─── - 4⋅C₇ + C₈ + ─── = 0\n", - " 1875 625 375 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle \\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0$" - ], - "text/plain": [ - "C₅ C₆ 8 \n", - "─── + ──── + C₇ - ─── = 0\n", - "625 1250 375 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAY4AAAAUCAYAAACajtrtAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAC9JJREFUeJztnHu01UUVxz9XTDE0KE1xpYGSDwgVlspDA0EUH1ReVCwNFJ9hGt20RFDbfDUVl68rKomPAE1XrVAwFHyCkuGjNBRFxQc3X4ghQoCiCfTHngM/Dr/feZ97Dq3zXeuuub+Z+c3sM/s3s2c/ZurWrVtHuSBpCHBneDzTzG7PUn8P4DSgP/BtoA2wDHgRuA+YaGafFkDHTsAHwM1mNjzkHQ8cAnQB9gO2A+42s8FZ2hoA/ALoBGwPLAKeB64zs6fT6g4FJmQhb62ZtYi80wS0S6i72MzaZmmv7GhOPkXKegENwEHAN4ClwDyg0cymhzrbAwOBAcA+wLeAL0K9CcAEM1sb028T1T/mVTGXmgs1+isDSbsAlwJHsmF9mwrIzD5J1duyjATsCtwIrAS2zVK3DhAwAtgKmANMBpbjE7o/0A84EehVADnHAFsAUyJ5F+MCYyXwHrB3tkYkXQVcAHyMD+YS4Duh/eMknWxmf4i8Mjf8rjj0Ag4FZsSULQcaY/JXZqOxnKgQn5B0MXAZPt4P4B/zDkBXoA8wPVQdBPwulM8C3gF2Ao4FbgeOkjTIzOJ2S1U55lB1c6msqNFfOUjqgNO7I3A/8BrQDd8oHynpYDP7GMokOMLgTcAX2PuAX2V55ffAUGA+cJKZvZjW3jbAefgiXQgGBlpmR/J+iQuMN3HNY1amBiS1xX/HYmBfM/soUtYXmIlL6vWCw8zm4sIjrr2UdnJrTPEyMxudiZ4Kodn5JGkQLjQeA441sxVpfX4l8rgA+CHwYFSzkDQKeA44Dhci98b0XZVjXoVzKSMiWnZfM3uigCYqSn8JsDnTPw4XGsPN7MZUpqTr8PXycmAYlE/jGI7vpvuENBGSzsMH+lWgR/rCAGBmnwGXh0HPC5JaBxruMbM1kTZnRerk0lQ7fDf8bFRopNqStAL4Zo40dQZ6AO8DD+byTqVRCT5J2gK4CvgUn4Rxff438v/MuLbN7ENJt+Affh/iBUe1omrmUrlRo79ykLQ7rg01ATenFRtwFjBE0vlmtqrkgkNSR2AMcIOZzZaU+LFL2hmfzF8CJ8QNdBRh0PPFAFxlvK+Ad6N4A7eXd5O0g5ktSRVI6o37SKbm2NZPQ3pHVJhFsLWkwbhtdBXwEjA7oW7ZUUE+HQTshqv6nwT/UmdgNfBcuk8pC1IC5suE8qoac6jKuVQ21OivOFLf1iPpfkAzWyHpb7hg6QE8XlLBIWlL4C7ctjwqh1cagJa40/LlUtISwUB8IXi0mEbMbKmkEcB1wHxJU3HzQQfcPPIoGwRCIsJOYzCwFre7x6EtPo5RLJR0qpk9WdgvKAoNVIZPB4Z0MfAC7vBeD0mzgePN7N+ZGg7f5cnh8aGEalU15lU6l8qJBmr05wxJDbjDPVfMNbOpGcr3CumChPI3cMGxJ/D4Fnl0nAt+gzssh+YoUetDmj5hSwJJLfHogBlmtrrY9sysEbeRbwmcCVyIO2TfxaMkPkp+ez1OwBk+w8zejSmfgDvP2gKt8MVyPNAemCFpv6J+RGGoD2lz82nHkA4DtgEOwzW7zsDDQG/gzzl0MSa8M93MHo4pr8Yxr6q51AyoD2mN/tzQgJuQcv2rj2skgtYhXZ5QnspvA2k+jixhiXFYH74qqRu+M7o2FxOCpG1x6bUOeDaPPlOmoV8D+wM7A4PMbHJM1f54FMqUmLK8IekC4ApgLHAT8CEejXUlcLekLmZ2QZZmzgrp+LhCM0t3uLwMDJO0EjgfGI3vzjPR2USBfIxpqxg+jcQF7V7A58AzwMiYHVkSn1JhynW4ZpFyNL4iaSC+OzpEUs+kb07ScHzcXgOGxNWpwjFvzrl0Dq4ptw9ZrwC/NbOsvrcsv3lWjO9wkpkNjWmnYPrT2hmFm4tuNrNzs9RtojrmSAv8+xqMr2WLgLuB0WaWZFbFzNrn008JUBfSdbCpc/wt3H6cKz6AjdTqBcAlOb6bciT/p4B45lZ4PPQEMjs6B+J+iaId0JL64I7aKWZ2XqTohcgidr6kW8zs7YQ2OuF2+/fYEEKaK27BF7HeOdQtiI8JKIZPffBIjb/jH96lwGOSOpnZ0ki9JD6l4sbfTo9OMbPPJD0MnI6HDG6ywIZF8QY8wqVfWp+5oNnHvAJz6T1cc34D59EpwFRJ+5vZS1nebWRTc0kXPKx6Eu5ojWJuQjvF0A+ApB64FSAbzSlUyxwZAZyDj/s8YF9gIr7RuizPtopBSqNonVD+tWi9jQSHmfUrsNOUxAVYnRCldJuk23BHXwNuzwbYRlKLfJyQZjaDcP4hKSIqSPIfADPNLEn9ygffD+kmYbtm9qmk5/AFsCsQKzjI7hTPhJQZrFW2ikXwMQ7F8OmI6HM4xLYcOBiYFvIy8en1kC5L6CIlWDaJUAk24Otx7aFfjmbEdFRizJt7Lt2flnWRpLOBnmRZhIPpdiOEcNxjcNPtEzmSUTD9oc/W+C79dNzElxXVMkfwjeRfzGxaeG6SNA3onumlMvg4UnNtz4TyPUK6ADy8tBT4HLgj4e+foc5T4flpgDCRm/BImj6ZGg9hmfmiN37ysSRmKmDrkCaF3Kbyv4grDHb8IbhT/I4C+u8Z0iShVBaUmE/b4d9cdOefiU+z8SiVPSRtFVPeOaRNaXSMwIXGXPw8QSFCAyoz5hWbS5JaSDoRF15zivgNeaEE9N8KTE4Kxy43iqR/DnCopL1DeSc8wimbRaKB0vo4Uhvi/uk0StoO3+x9hpubS3OOIzjvzogrkzQa34VPsk2vSWgMf+MkHWNmr6W9W4c7TU/Fncr54Fh8kU7fURWKvwLnAmdJGm9m70foPAof2NUkT7hBwNeBBxKc4kj6LrAo3aQiqR3uU4HIAcNmRCOl4VMjvpg/E8lL5JOZLZH0J+An+E7y4kifhwNH4BrMQ5H8S3CT2PNA/2zmqWob80rMJUn74EKoJbACqDezecX+ljzRSAH0SzoTP0wX679qRjRS2PiPwTdU8yWtwdfky81sXKbOSu3jMLO3JD2C+xvPwW8qSEG41j3ezFZBGa8cyRFjcZveacC8QPgCPOZ+V+B7wC7APQW0XQ/MMbPFcYWS6tkghVN3EfWUNDH8v8TMoqd0J+Onlw8DXpU0BXeOd8TNWHXAhRaO5Mcg5RSPOymewiDgQkmzgIX4JO6An3Foie9CrsnwfrlQNJ8kXY1rF73SVPl6MvAJP2XbHTeh9MZPgLfDzYJr8HubloU+TsGFxhpc0A+PMfU0mdnEyHO1jnm+KIZHr+O+iTb46fo7JfVp5rDYvOmXtBcerNLLzGI1/WZEoeN/PL4xOgkPTOgC3CBpoZkVYpkoBj/DN75jJfXDDzJ2B/riv+WiVMWKCg7zO4NOl3Qv7tjqBhyO38mzCN8FPYjfT5QzJB2IM+n6DNW64A6pKHYPfwD/InK9g5mtlXQ0Lo1/jC9cX8XNLtOBsWb2SAI9HfEPJ5tTfBYegdQVN5O0wu37T+EO07ss/p6lsqJYPkm6Fo8a6Wtmb0bys/LJzD6S1B3XNgbiB5BWhP6uNLOo9rJbSFvgqnwcnsSdjylU5Zjni2J4FBbdFF/+EfjSQILmUw4USH9P/M6ylyMbhBZAb0nDgFZm9nkV0w9wLXCNmf0xPM8L2u4oCjNpF4ygdRzAhksOj8ZpH4tfcrheK68r5+24zQFJ60gLx5V0BTAS2N3MFlaMuBqQNBb4ES405qeV1fhUhZA0E/ggKfy0WiCpDb7xiGICHiF2BfBKtQt9SR8DZmY3RfJGAmeYWYfKUZYZlTZVFYQQNx29JKy9pC7AUjN7B9+ZvlhbjCoLSeNwTaMeWCq/KBJgpZmtpManikPSGHwn/C5uaz8Jd/AOqCBZOSGYKJdF8yStwteBzeX0+TTcVLoQN1V1xc2zd2Z8q8LYLAUHcAAbh8VeHdJJ+Enbjs1PUg0xODukj6flCz/gVONT5dEWd/63xQMNXgKOsvgT9jWUHj/Hz2ukbqZdBNyGm4uqFpu9qaqGGmqooYbmRanvqqqhhhpqqOH/HDXBUUMNNdRQQ174Hyg4Ej1GSXI4AAAAAElFTkSuQmCC", - "text/latex": [ - "$\\displaystyle - \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0$" - ], - "text/plain": [ - " 4⋅C₁ C₂ \n", - "- ──── - ─── - 4⋅C₃ + C₄ - C₈ = 0\n", - " 1875 625 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle \\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0$" - ], - "text/plain": [ - "C₁ C₂ \n", - "─── + ──── + C₃ - C₇ = 0\n", - "625 1250 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAUCAYAAAAdtqaHAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAABSNJREFUeJztmmuoVUUYhp9jIpmaQhQIWqFp5A8pMjO8kB5QqR8eioxCRbtAqdjJbmjY62cYgVhHJSG01CQhsDAKBFG0sDQkVDKzk5L4I8HQsjSVLrsf36zarc5ae+3bWUfYDyxm77m+s5j5Zuab1VQoFEjCzKYD74S/j0tam5J3CPAIMBG4HugH/AwcBD4A1kv6LbGxnGnozwczGwAsASYD1wAngS2ASfopytc9pYKBwCrgHNA7JV8TYMALQA/gc2AzcBa4AX9xzcBDwNgq+lQXGvrzw8wG43qvAz4EjgAjgaeAyWY2WtJpSBioofPrgNP4bHw2pb23gZnAYeBhSQdjdfUE5gM3Vd6lZMxsZtA6XtKuCqrIVX8NuJz1r8YH6TxJq6JIM3sNeBpYCjwB0C2hgnnABGAWcD6pFTObj7+kb4BR8ZcEIOmCpKXA7Ep6Uk8a+vPDzAbh1v448EYsWfi4m25mvaCDgWpmtwCvAiskfZrSUH98xP8BTJX0a5owSReyd6P+NPTnzoQQbpP0V3FC6MtnwFXAKIgt/WbWHdgInAAWlmioFbgSWCfpUNWyO59WGvozY2at+AEtKwckbUlJvzmE7Qnp3+EWdyiwI75HfQm4DRiTYQa2hHBjiXxdlZYQNvRnoxU/nGVlA356T6JvCM8mpEfx/aDIoprZSNyKLpe0J02BmfXGR3oB+KKU4ljZccBzwO1Af+ABSZszlj1O8svaaWbxuA2SZnZQTzX6FwD34RbhErAXWJDFqpXQ3xHvSprWQT0V6w/l++Pbu3twj85RYK6kT5LKSLqx3HaqpCmEBQgDtWjJbwcWZajk2hD+UoFvrhfu21sHvF9m2Tb+v/zcCkzBZ/DxWNqBhHqq0X83flrdh7/MJcB2Mxsm6UyJsseAi2W09UNCfMX6zawfvv/bDdwL/AgMAk6VU08NiCxm34T0q4vzRRY1mqEAFzuwTABrzGwNsAJ4JcT1NLMrJP2ZVZ2krcBWgIR20sq2xeOCe2oK7tDelbGqyJNRif5Jsfan4y9zNPBRibLNWdspQcX6geeBk5JmFMV9X6pQHfao34ZwaEL6kBC2w7+n/kvAWwnP/pBnd/i/R9Ip3Hr1wC1MImaW5ALLjRrr74O/x1LWtGZUqb8F2Gtmm8zslJkdMLO5wXeeRivuNsr6tJSob2cIJ8Y1mlkffOJfwLdWblHDwemxhI4uxg9YG2JXqG3hWW1mUyQdiZVrwq/FZgFTS4jOgzZqo78N32LsrYfIEu22Ub7+QcAc4HVgEr51WoXvBeP+zH+o9R5V0jEz24af7OcEDRGGbxHflHQeUq5QM7ASGI7fL38VGm0HfgcGAmOAAcCmKtqoJ1XrN7NlwDhgbJnLby2oVH834EtJC8L//WY2FB8siQO1TszGr1BXmlkzfnFxJzAe78uLUcaKl2VJBUmP4hvyj/GZOQe3zMOBPfitybxK26gn1eo3s+XADKBZ0tFOkPwfqtB/Evg6FneY8rwRNUHSMWAEsB4foM8Ag/FJeFd0zw/QlPb1VL0xswJluKe6Cma2EngQ/77gcN56ysHMNgEDJY0tinsZuF/SsPyUpdPpAzX4AKMPJPbjPtXtwBlJJzpVTAWY2WpgGn5YKB6k5ySdy0VUGZjZHfhyuxh4Dz9/rAUWSurspT8zeZzIR+ADNPImLAu/l+SgpRKexE/6O/BlNHrSvjDrMkjah0+yqcAh/HuBRbhvuMuS69LfoEFWupyPs0GDjmgM1AaXBX8Db+ZLKnj2sa4AAAAASUVORK5CYII=", - "text/latex": [ - "$\\displaystyle 4 C_{1} + C_{2} - C_{6} = 0$" - ], - "text/plain": [ - "4⋅C₁ + C₂ - C₆ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAUCAYAAABs4ghcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA6lJREFUeJztmV2IVGUYx3+zimhWCn0KWy2adhGEkNSGFdWCCF20NwlFQqUXYmCTEhEh//6CV4aIktFNukR5Y2EUCEJ4VZt3iWF+sPkBfZOlohukTRfve7az48x4zsyZ2R3YHxwezvv5f+Y57+eUKpUKtbC9EHgZWAbcDcwF/gIOA58CuyVdrll5gulW7bZ7gU3AcuAW4GdgH2BJf6bLTq9RuQQYeAOYAXwN7AXOA/cQfowB4DngsXY50Qxdrn0BQe/twGfAMeAh4FVgue2lkv5Iyl8TOOAD4EXgKPC8pMNVHcwC1gP3tsOBFulm7TsJQVsnaUeSaHsr8BqwGViTpPeka9peT3D8e6C/2nEASaOSNgNr26G+Wbpc+3zCbHAaeLcqW8AlYKXt2UliT6ryPEJUrwArJF1s1Jmk0WJkt043a488Fe0BSf+mM6IvXwE3AP1JenqqLAMzgV2SvmuvzsIp02HttsuETU9WvpW0r07efdGeqJN/kjAiFwFfwvjADUb7YQ4xk4XBaDupvUzY8GRliLBDrMWcaM/XyU/S5yYJ0wFs30iIZgU4lFWJ7ceB14EHgXnAs5L2Zqx7mnyOfyTphRrtNKU91n2bsIak+VXSnderK6kvT18tUop27OyWjLjbor2Q83wzm3A22gV8klPMCPB3jvI/1UlvVnvCceCJ1PvVJtpolWREzamTf3NVubHAXYp2lu1pkjKJl7Qf2A9gO5dSSQO5KtSnKe0prkj6JW+nBa9xx6NdVCd/YbRja2APgKTfCFvRGYz/+q7Bdk+j/E5TgPb5tn+0fcr2Htt9GbsuE6bZrM9gg7YORrusWqPtm4ClwCjwTZKe3pxsi89O289IOlbVQIlwFfMSsCKjc51iG81pPwSsJHzJdwBvAsO275d0rlGHRa5xkkZsHyDsHF8BdqSyTViS3peUzC7jArcdeIBwx3ckNnQC+Ae4C3gU6AU+LkpwgTSlPU71CUdsDwM/EA7yW9svexxrCVde220PEC4SHgaeJPjyVrrw2LCUVJG0Cnga+AJYTIj+asKPMkxwaF27PchLUdrjF32U/9eUjiFpBFgC7CYEbAOwgPBRPpK+pwQo1ft3IC+2K+Q4DkxGbM8ETgHvSdo00XoaUeuSOTPxDJW+sO2zvRg4J+lsK213AtvvAJ8DZwkXvBsJ68nQROrKQkuBIwztg6n3LdEOEaamyU4vsAe4FfidsGvrl3RmQlVloLCpcorOMqnOZFNkZypwXcp/TvVqwtp6oi4AAAAASUVORK5CYII=", - "text/latex": [ - "$\\displaystyle C_{1} - C_{5} = 0$" - ], - "text/plain": [ - "C₁ - C₅ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C_{4} = 0\n", - "C_{2} = 0\n", - "- \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0\n", - "\\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0\n", - "- \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0\n", - "\\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0\n", - "4 C_{1} + C_{2} - C_{6} = 0\n", - "C_{1} - C_{5} = 0\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "%config InlineBackend.figure_formats = ['svg']\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = 10 \n", - "EI = 5000 \n", - "EA = 20000 \n", - "F = 150\n", - "\n", - "q_AC = 0\n", - "q_BC = q\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", - "\n", - "V_AC = -sym.integrate(q_AC, x) + C_1\n", - "M_AC = sym.integrate(V_AC, x) + C_2\n", - "kappa_AC = M_AC/EI\n", - "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", - "w_AC = -sym.integrate(phi_AC, x) + C_4\n", - "\n", - "V_BC = -sym.integrate(q_BC, x) + C_5\n", - "M_BC = sym.integrate(V_BC, x) + C_6\n", - "kappa_BC = M_BC/EI\n", - "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", - "w_BC = -sym.integrate(phi_BC, x) + C_8\n", - "\n", - "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", - "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", - "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", - "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", - "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", - "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", - "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", - "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", - "\n", - "display(eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8)\n", - "for eq in [eq1, eq2, eq3, eq4, eq5, eq6, eq7, eq8]:\n", - " print(sym.latex(eq))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle \\left\\{ C_{1} : \\frac{35}{8}, \\ C_{2} : 0, \\ C_{3} : - \\frac{1}{150}, \\ C_{4} : 0, \\ C_{5} : \\frac{35}{8}, \\ C_{6} : \\frac{35}{2}, \\ C_{7} : \\frac{1}{3000}, \\ C_{8} : \\frac{13}{750}\\right\\}$" - ], - "text/plain": [ - "⎧ 13 ⎫\n", - "⎨C₁: 35/8, C₂: 0, C₃: -1/150, C₄: 0, C₅: 35/8, C₆: 35/2, C₇: 1/3000, C₈: ───⎬\n", - "⎩ 750⎭" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "'\\\\left\\\\{ C_{1} : \\\\frac{35}{8}, \\\\ C_{2} : 0, \\\\ C_{3} : - \\\\frac{1}{150}, \\\\ C_{4} : 0, \\\\ C_{5} : \\\\frac{35}{8}, \\\\ C_{6} : \\\\frac{35}{2}, \\\\ C_{7} : \\\\frac{1}{3000}, \\\\ C_{8} : \\\\frac{13}{750}\\\\right\\\\}'" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "display(sol)\n", - "sym.latex(sol)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " 2025-07-02T16:58:07.214407\n", - " image/svg+xml\n", - " \n", - " \n", - " Matplotlib v3.10.3, https://matplotlib.org/\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n" - ], - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "M_numpy = sym.lambdify(x, M_BC.subs(sol))\n", - "x_numpy = np.linspace(0,4,100)\n", - "plt.figure()\n", - "plt.plot(x_numpy,M_numpy(x_numpy))\n", - "plt.xlabel('$x$')\n", - "plt.ylabel('$M_{BC}$');\n", - "ax = plt.gca()\n", - "ax.spines['right'].set_color('none')\n", - "ax.spines['top'].set_color('none')\n", - "ax.spines['bottom'].set_position('zero')\n", - "ax.spines['left'].set_position('zero')\n", - "ax.invert_yaxis()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\\left[\\begin{matrix}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{matrix}\\right]\n", - "\\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]\n" - ] - } - ], - "source": [ - "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8), (C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "print(sym.latex(A))\n", - "print(sym.latex(b))\n", - "A = np.array(A).astype(np.float64)\n", - "b = np.array(b).astype(np.float64)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "A = np.array([\n", - " [0, 0, 0, 1, 0, 0, 0, 0],\n", - " [0, 1, 0, 0, 0, 0, 0, 0],\n", - " [0, 0, 0, 0, -4/1875, -1/625, -4, 1],\n", - " [0, 0, 0, 0, 1/625, 1/1250, 1, 0],\n", - " [-4/1875, -1/625, -4, 1, 0, 0, 0, -1],\n", - " [1/625, 1/1250, 1, 0, 0, 0, -1, 0],\n", - " [4, 1, 0, 0, 0, -1, 0, 0],\n", - " [1, 0, 0, 0, -1, 0, 0, 0]\n", - "])\n", - "\n", - "b = np.array([\n", - " 0,\n", - " 0,\n", - " -8/375,\n", - " 8/375,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 4.37500000e+00],\n", - " [ 0.00000000e+00],\n", - " [-6.66666667e-03],\n", - " [ 0.00000000e+00],\n", - " [ 4.37500000e+00],\n", - " [ 1.75000000e+01],\n", - " [ 3.33333333e-04],\n", - " [ 1.73333333e-02]])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.linalg.solve(A,b)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "text/latex": [ - "$\\displaystyle \\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]$" - ], - "text/plain": [ - "Matrix([\n", - "[ 4.375],\n", - "[ 0],\n", - "[-0.00666666666666667],\n", - "[ 0],\n", - "[ 4.375],\n", - "[ 17.5],\n", - "[0.000333333333333334],\n", - "[ 0.0173333333333333]])" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]\n", - "\\left[\\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 4.375\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -0.00666666666666667\\\\0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 4.375\\\\0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 17.5\\\\0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0.000333333333333334\\\\0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0.0173333333333333\\end{matrix}\\right]\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "\n", - "A_sym = sym.Matrix(A)\n", - "b_sym = sym.Matrix(b)\n", - "\n", - "sol_sym = A_sym.LUsolve(b_sym)\n", - "\n", - "display(sol_sym)\n", - "\n", - "print(sym.latex(sol_sym))\n", - "\n", - "identity = sym.eye(sol_sym.rows)\n", - "rr_augmented = identity.row_join(sol_sym)\n", - "print(sym.latex(rr_augmented))" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C_1 = 35/8 ≈ 4.37500000000000\n", - "C_2 = 0 ≈ 0\n", - "C_3 = -1/150 ≈ -0.00666666666666667\n", - "C_4 = 0 ≈ 0\n", - "C_5 = 35/8 ≈ 4.37500000000000\n", - "C_6 = 35/2 ≈ 17.5000000000000\n", - "C_7 = 1/3000 ≈ 0.000333333333333333\n", - "C_8 = 13/750 ≈ 0.0173333333333333\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = 10 \n", - "EI = 5000 \n", - "EA = 20000 \n", - "F = 150\n", - "\n", - "q_AC = 0\n", - "q_BC = q\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", - "\n", - "V_AC = -sym.integrate(q_AC, x) + C_1\n", - "M_AC = sym.integrate(V_AC, x) + C_2\n", - "kappa_AC = M_AC/EI\n", - "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", - "w_AC = -sym.integrate(phi_AC, x) + C_4\n", - "\n", - "V_BC = -sym.integrate(q_BC, x) + C_5\n", - "M_BC = sym.integrate(V_BC, x) + C_6\n", - "kappa_BC = M_BC/EI\n", - "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", - "w_BC = -sym.integrate(phi_BC, x) + C_8\n", - "\n", - "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", - "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", - "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", - "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", - "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", - "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", - "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", - "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", - "\n", - "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "for k, v in sol.items():\n", - " print(f\"{k} = {v} ≈ {v.evalf()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "::::::{codex-end}\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Meer voorbeelden\n", - "Het voorbeeld in hoofdstuk 2.7.3 van het boek Mechanica, spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013` richt zich op extensie. Voorbeeld 3b in hoofdstuk 8.2 is een buigingsprobleem.\n", - "\n", - "## Instructies in collegevorm\n", - "\n", - "Dit onderwerp is [in les 3](https://collegerama.tudelft.nl/Mediasite/Channel/public-ceg-ctb2210/watch/757766716fcb45718fca678dadaa396d1d?sortBy=most-recent) gepresenteerd in collegevorm van 0:40:00 tot 0:48:30\n", - "\n", - "## Opgaves\n", - "\n", - "- Opgaves 2.48 en 2.50 in hoofdstuk 2.9 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter2/).\n", - "- Opgaves 8.7, 8.10, 8.11 in hoofdstuk 8.6 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter8/)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/book/instructie_2.ipynb b/book/instructie_2.ipynb deleted file mode 100644 index 0382117..0000000 --- a/book/instructie_2.ipynb +++ /dev/null @@ -1,1470 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "````{margin}\n", - "```{attributiongrey} Attribution\n", - ":class: attribution\n", - "\n", - "Deze pagina is aangepast van https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro.ipynb\n", - "\n", - "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_2/session_1/intro_data/Tekening1.vsdx\n", - "\n", - "```\n", - "```` " - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "# Instructie_new\n", - "\n", - "In constructiemechanica 2 heb je geleerd hoe je met differentiaalvergelijkingen voor statisch bepaalde constructies de krachtsverdeling en vervormingen kan vinden. Precies dezelfde aanpak werkt ook voor statisch onbepaalde constructie, met als enige verschil dat we de inwendige krachten niet kunnen vinden zonder ook de vergelijkingen en randvoorwaarden voor verplaatsingen op te stellen. Dat doen we volgens de volgende stappen:\n", - "\n", - "::::::{prf:algorithm} Differentiaalvergelijkingen toepassen\n", - ":nonumber: true\n", - ":label: differentiaalvergelijkingen_algoritme_2\n", - "\n", - "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", - "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", - "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", - "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", - "\n", - "::::::\n", - "\n", - "Als voorbeeld bepalen we de een simpele statisch onbepaalde constructie:\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_0_2\n", - "\n", - "Gegeven een constructie zoals hieronder weergegeven:\n", - "\n", - "```{figure} intro_data/structure.svg\n", - "---\n", - "align: center\n", - "---\n", - "Constructie, $EI = 5000 \\ \\rm{kNm^2}$\n", - "```\n", - "::::::\n", - "\n", - "1. Eerst definiëren we de segmenten zonder discontinuïteiten. Daarna kan elk segment afzonderlijk worden getekend, waarbij de belastingen, coördinatenstelsels, afmetingen en snedekrachten aan de randen van elk segment worden aangegeven.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_1_2\n", - "\n", - "In dit voorbeeld moeten we element AB in twee delen opsplitsen omdat de verdeelde belasting een discontinuïteit veroorzaakt.\n", - "\n", - "Voor AC leidt dit tot:\n", - "\n", - "```{figure} intro_data/FBD_AC.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram element AC\n", - "```\n", - "\n", - "Voor BC leidt dit tot:\n", - "\n", - "```{figure} intro_data/FBD_BC.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram element BC\n", - "```\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "2. Nu kunnen we voor elk element de belastingsvergelijking opstellen en volgens de relaties van de differentiaalvergelijkingen alle krachtsgrootheden en verplaatsingen opstellen. Daarbij kunnen we direct ook de randvoorwaarden opstellen.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_3_2\n", - "\n", - "In dit voorbeeld is er enkel invloed door buiging, dus hebben we aan deze differentiaalvergelijking genoeg: $ -EI \\cfrac{{d}^4w}{{d}x^4} = -q_z$. De differentiaalvergelijkingen voor rek $ EA \\cfrac{{d}^2u}{{d}x^2} = -q_x$ en torsie $ GI \\cfrac{{d}^2\\varphi_x}{{d}x^2} = -q_T$ hebben we in dit geval niet nodig.\n", - "\n", - "**Element AC**\n", - "\n", - "Omdat dit element onbelast is, geeft de belastingsvergelijking voor dit element:\n", - "\n", - "$$q_{z,{\\rm{AC}}}=0$$\n", - "\n", - "wat leidt tot de vergelijkingen:\n", - "\n", - "$$ V_{\\rm{AC}} \\left(x\\right) = C_1$$\n", - "\n", - "$$ M_{\\rm{AC}} \\left(x\\right) = C_1 \\cdot x + C_2 $$\n", - "\n", - "$$ \\kappa_{\\rm{AC}} \\left(x\\right) = \\cfrac{C_1 \\cdot x + C_2}{5000}$$\n", - "\n", - "$$ \\varphi_{\\rm{AC}} \\left(x\\right) = \\cfrac{\\cfrac{1}{2}\\cdot C_1 \\cdot x^2 + C_2 \\cdot x}{5000} + C_3$$\n", - "\n", - "$$ w_{\\rm{AC}} \\left(x\\right) = \\cfrac{-\\cfrac{1}{6}\\cdot C_1 \\cdot x^3 - \\cfrac{1}{2} C_2 \\cdot x^2}{5000} - C_3 \\cdot x + C_4$$\n", - "\n", - "Vanwege de oplegging bij A kan een randvoorwaarde worden geformuleerd als:\n", - "\n", - "```{math}\n", - ":label: BC_1\n", - "w_{\\rm{AC}} \\left(0\\right) = 0\n", - "```\n", - "\n", - "Omdat A ook een onbelast uiteinde is, kan een andere randvoorwaarde worden geformuleerd als:\n", - "\n", - "```{math}\n", - ":label: BC_2\n", - "M_{\\rm{AC}} \\left(0\\right) = 0\n", - "```\n", - "\n", - "Als dit uiteinde belast werd kon met een krachten- of momentenevenwicht ook een relatie gevonden worden.\n", - "\n", - "**Element BC**\n", - "\n", - "Omdat dit element belast is, geeft de belastingsvergelijking voor dit element:\n", - "\n", - "$$q_{z,\\rm{BC}}=10$$\n", - "\n", - "wat leidt tot de vergelijkingen:\n", - "\n", - "$$ V_{\\rm{BC}} \\left(x\\right) = -10 \\cdot x + C_5$$\n", - "\n", - "$$ M_{\\rm{BC}} \\left(x\\right) = -5 \\cdot x^2 + C_5 \\cdot x + C_6 $$\n", - "\n", - "$$ \\kappa_{\\rm{BC}} \\left(x\\right) = \\cfrac{-5 \\cdot x^2 + C_5 \\cdot x + C_6}{5000}$$\n", - "\n", - "$$ \\varphi_{\\rm{BC}} \\left(x\\right) = \\cfrac{-\\cfrac{5}{3} \\cdot x^3 + \\cfrac{1}{2}\\cdot C_5 \\cdot x^2 + C_6 \\cdot x}{5000} + C_7$$\n", - "\n", - "$$ w_{\\rm{BC}} \\left(x\\right) = \\cfrac{\\cfrac{5}{12} \\cdot x^4 -\\cfrac{1}{6}\\cdot C_5 \\cdot x^3 - \\cfrac{1}{2} C_6 \\cdot x^2}{5000} - C_7 \\cdot x + C_8$$\n", - "\n", - "Vanwege de inklemming bij B kunnen twee randvoorwaarden direct worden geformuleerd:\n", - "\n", - "```{math}\n", - ":label: BC_3\n", - "\\varphi_{\\rm{BC}} \\left(4\\right) = 0\n", - "```\n", - "\n", - "```{math}\n", - ":label: BC_4\n", - "w_{\\rm{BC}} \\left(4\\right) = 0\n", - "```\n", - "\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "3. Er zijn nog meer vergelijkingen nodig om alle onbekende integratieconstantes op te lossen, deze kunnen we vinden door de overgangen tussen de verschillende elementen te analyseren waarbij zowel verplaatsingen als krachten in overeenstemming moeten zijn.\n", - "\n", - "::::::{prf:example}\n", - ":nonumber: true\n", - ":label: df_example_4_2\n", - "\n", - "Bij C kan een randvoorwaarde worden geïdentificeerd uit de consistentie van verplaatsingen: zowel de neerwaartse als rotatieverplaatsing moeten hetzelfde zijn als de neerwaartse en rotatieverplaatsing aan de linkerkant van element BE:\n", - "\n", - "```{math}\n", - ":label: BC_5\n", - "w_{\\rm{AC}} \\left(4\\right) = w_{\\rm{BC}} \\left(0\\right)\n", - "```\n", - "\n", - "```{math}\n", - ":label: BC_6\n", - "\\varphi_{\\rm{AC}} \\left(4\\right) = \\varphi_{\\rm{BC}} \\left(0 \\right)\n", - "```\n", - "\n", - "Ten slotte moet er ook consistentie zijn met de snedekrachten, zoals getoond in het vrijlichaamdiagram:\n", - "\n", - "```{figure} intro_data/FBD_C.svg\n", - ":align: center\n", - "\n", - "Vrijlichaamdiagram knoop C\n", - "```\n", - "\n", - "Verticaal evenwicht geeft:\n", - "\n", - "```{math}\n", - ":label: BC_7\n", - "\\sum {{{\\left. {{F_{\\rm{v}}}} \\right|}^{\\rm{C}}} = 0} \\to -{V_{\\rm{AC}}}\\left(4\\right) + V_{\\rm{BC}}\\left(0\\right) = 0\n", - "```\n", - "\n", - "Momentevenwicht geeft:\n", - "\n", - "```{math}\n", - ":label: BC_8\n", - "\\sum {{{\\left. {{T}} \\right|}_{\\rm{C}}^{\\rm{C}}} = 0} \\to -{M_{{\\rm{AC}}}}\\left(4\\right) + M_{\\rm{BC}}\\left(0\\right) = 0\n", - "```" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "4. Er zijn nu evenveel vergelijkingen als onbekenden opgesteld. Deze kunnen nu worden opgelost en ingevuld in de opgestelde vergelijkingen voor het gewenste resultaat. Omdat het aantal onbekenden bij statisch onbepaalde constructies als snel uit de hand loopt wordt wordt aangeraden om je rekenmachine of een symbolische wiskundetool te gebruiken om dit op te lossen. Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python. Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt:\n", - "\n", - "\n", - "::::::{prf:example-start}\n", - ":nonumber: true\n", - ":label: df_example_5_2\n", - "\n", - "::::::\n", - "\n", - "Er zijn 8 randvoorwaarden geformuleerd met 8 integratieconstanten. Deze kunnen nu worden opgelost. \n", - "\n", - "Het invullen van de randvoorwaarden geeft:\n", - "\n", - "$$\n", - "\\begin{align}\n", - "C_{4} = 0 \\\\\n", - "C_{2} = 0 \\\\\n", - "- \\cfrac{4 C_{5}}{1875} - \\cfrac{C_{6}}{625} - 4 C_{7} + C_{8} + \\cfrac{8}{375} = 0 \\\\\n", - "\\cfrac{C_{5}}{625} + \\cfrac{C_{6}}{1250} + C_{7} - \\cfrac{8}{375} = 0 \\\\\n", - "- \\cfrac{4 C_{1}}{1875} - \\cfrac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0 \\\\\n", - "\\cfrac{C_{1}}{625} + \\cfrac{C_{2}}{1250} + C_{3} - C_{7} = 0 \\\\\n", - "4 C_{1} + C_{2} - C_{6} = 0 \\\\\n", - "C_{1} - C_{5} = 0 \\\\\n", - "\\end{align}\n", - "$$\n", - "\n", - "Het oplossen hiervan geeft:\n", - "\n", - "$$\n", - "\\begin{align}\n", - "C_{1} = \\cfrac{35}{8} = 4.375 \\\\\n", - "C_{2} = 0 \\\\\n", - "C_{3} = - \\cfrac{1}{150} \\approx -0.006667 \\\\\n", - "C_{4} = 0 \\\\\n", - "C_{5} = \\cfrac{35}{8} = 4.375\\\\\n", - "C_{6} = \\cfrac{35}{2} = 17.5 \\\\\n", - "C_{7} = \\cfrac{1}{3000} \\approx 0.0003333\\\\\n", - "C_{8} = \\cfrac{13}{750} \\approx 0.01733\n", - "\\end{align}\n", - "$$\n", - "\n", - "Nu is de volledige krachtenverdeling en verplaatsingen van de constructie opgelost. Bijvoorbeeld de momentenverdeling in BC die kan worden uitgezet als:\n", - "\n", - "```{figure} intro_data/Momentenlijn.svg\n", - ":align: center\n", - "\n", - "Momentenlijn BC\n", - "```\n", - "\n", - "De krachtsverdeling en verplaatsingen kunnen no ook \n", - "\n", - "**Oplossen met digitale hulpmiddelen**\n", - "Om het stelsel vergelijkingen op te lossen met behulp van numerieke hulpmiddelen, kun je de vergelijkingen opschrijven als een matrixformulering $Ax=b$ die eenvoudig kan worden opgelost door bijvoorbeeld je grafische rekenmachine of in python.\n", - "\n", - "\n", - "$$\\left[\\begin{array}{cccccccc}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{array}\\right] \\left[ \\begin{array}{cccccccc} C_1\\\\C_2\\\\C_3\\\\C_4\\\\C_5\\\\C_6\\\\C_7\\\\C_8 \\end{array} \\right] = \\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]$$\n", - "\n", - "[Hier](rref_ti_84) is uitgelegd hoe je dit op een grafische rekenmachine kan oplossen.\n", - "\n", - "Als alternatief kan een symbolische programmeertaal worden gebruikt om de vergelijkingen exact op te lossen. Als voorbeeld wordt het [`sympy`](https://docs.sympy.org/latest/index.html) pakket in `Python` gebruikt.\n", - "\n", - "Beide opties zijn hieronder getoond" - ] - }, - { - "cell_type": "code", - "execution_count": 1, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAs9JREFUeJzl102o1FUYx/HPaEh2q3sxCgKtqNRdS7tgQi8g0SZXLgLBCiEsYnITFPTwBLqMUBRq0S2iVhEGtRF6WVTULktLbwTtgsDK6nbpzXFxztTcaeY2M3eGS/SF4YHznJfn9z/nPOeZRqvV0ovM3IwHsBPXYQY/4CRex4sR8UvPwatEZm7E07gbV+EbHEdGxPftfo1u0ZnZQOJxrMOH+BTncb3yETbg/YjYMWkhg5KZNymxXoM3cAbbcAfOYntEnINLeox/AXvxOe6LiJNdk6/HAdw8ofhH5Zgi+NGIONJuzMxn8BgO4iFY0zkqMw8ogr/AbLdgiIjFiDiI/ZOKflgy80blBH6No13uwAL2ZOYUHaIz81rla/yB3RHx03ILRcTi+MJeMXdWeyIiLnQ6qo4PcBlmWXq8m7gUcxFxatJRZmZTSY6D8klEHO/j21rtfB//l8pJ2IK3O0XvqvblIQJZCU0lMQ7KS0om7sV0tef7+NvtM9SdzszLla/QwsdDBPIXmfmEcj2ORsQj/9Y/Im4YZZ0RaVTb4u87fXW1P47y9mbmLPYpT9tq0N7J6T7+Kzv7tY/3QrXrM3NtRPw56GqZOY1X8CCeGmJc0/ju9Nlqt/Txb652nrrTEfGtku7X4fblVs7MNV1Nz+O1iHhnuXE9aCrPyaC/XcvM9W61O7vjy8wrsB2L+Iil2fvZ+juWmfdGxJmuwQ2lvLsfu2vbPqVI2TOw1Mo473REfJWZJ5QM/TCOdLgTU3guIhZYKvowblHq7c/qJPP4HZtwGzbiVcjMrTiEHRHx27gErID9Shl6ODPvUgqsW5UydB5Ptjv2qr3vUZLSNiXB/awU7qfxFt6MiHOZuRdz6Lz/a5UMeQFTEfHrBMT1JTM36f+H47t2v3+IHmKBGWXnO5lTCoFDOB0Ro00+YUYW3YvMfA+nBnmnV5PuTPy/YKw7/V/hImUh8qiSnFTZAAAAAElFTkSuQmCC", - "text/latex": [ - "$\\displaystyle C_{4} = 0$" - ], - "text/plain": [ - "C₄ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAD0AAAASCAYAAADomNYKAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAAt9JREFUeJzV10+IVXUUB/DPTBGZmWIYBFpSaruWJZmRBSZtkhYGkZBBUAoxSRDS4nACV1HISANt+kPQMoxqIw1tKmqn/TGdCNwFQX+smYbIei1+99ad17vje773kL5wObx7fud3zvf3O+e8cyc6nY5eyMzNeAw7cQPW4GecxNt4PSJ+62l8iZCZ6/E8duFafIdjyIj4qV430U06MyeQeBZX4BN8jnO4UTmEtfgoIraPm0i/yMyblVivwzs4jduwA2ewLSJ+gMt72L+KR3EKD0fEya7NV+AgNo0p/ovFjEL4qYg4Wr/MzJfwNA7jCZhsWmXmQYXw19jaTRgiYjEiDmP/uKIfFJl5k5KBZ/FylzqwgL2ZuZIG6cy8XjmN89gTEb8u5ygiFkcX9tC4p5LHI+KvpqLi8TGuwlaWpvcUrsRrEfHluKPMzCmlOfaLExFxrEV3SyXnWvTfKJmwBbNN0rsr+eYAgQyDKaUx9os3lE7cC6srea5FX79fQ3XTmXm1cgodfNZvFJl5CA8qJ/07PsWhfjIlIjb262cEmKhkh39rel0lfxnwv/dupWveodTVeXyQmWuHj3Mg1De5ukV/TXNdnd4LlVyRmZdFxJ/9eIqI+5q/M3NvtfE2vLuc7Yhr+kwlt7ToN1dyjop0RHyfmWexUbm92TbPmTnZ3SEbWKVkz49t9g1MGV1Nf1jJnd3xZeYq5RIWlfJb0r2PVM9MZj4QEaebu1aT2i7sw54W50dwot58OYyypiPi28w8rnToAzjaUCdW4pWIWGAp6WncqszbX1SbzOEPbMCdWI+3ejnOzBdwF7b3Wx4jxn5lDJ3OzHuVAet2ZQydw3P1wl6z9/14XJlb12FeGdy/wvt4r55hGzYv4hHsiIhT4+F0YWTmBu0fHP+U3H9IX4SjaTzkEhMeBEORzswZ5YZ3Kx8oNeYjYn640MaHyQsvWRZPKh17Vkml+nlmyH3HiqHT+/+IvwHtQ/pzJbnwjAAAAABJRU5ErkJggg==", - "text/latex": [ - "$\\displaystyle C_{2} = 0$" - ], - "text/plain": [ - "C₂ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle - \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0$" - ], - "text/plain": [ - " 4⋅C₅ C₆ 8 \n", - "- ──── - ─── - 4⋅C₇ + C₈ + ─── = 0\n", - " 1875 625 375 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle \\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0$" - ], - "text/plain": [ - "C₅ C₆ 8 \n", - "─── + ──── + C₇ - ─── = 0\n", - "625 1250 375 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle - \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0$" - ], - "text/plain": [ - " 4⋅C₁ C₂ \n", - "- ──── - ─── - 4⋅C₃ + C₄ - C₈ = 0\n", - " 1875 625 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAATQAAAAUCAYAAAADWsEdAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAACUVJREFUeJztnH+wVVUVxz9PiHCQYLIUJw3E0H5o4SghGgiiqDHleyQ2GgREFkZDL7QR0lp8LdPGzBcm2UzDjyxnmtRwCsgf8ZKKyn4MP8SUVF7mQBgiBIqZSH+sfeG+wzn33XPvOYdH3e/MnX3vOWfvvc53r7PP2mutfZv27dtHHCQNAT4BjAPeDvQHdgBrgfuAxWb2cmzlCpB0LLAZuMPMZkXOjQRagbOBNwPbgfVAm5ktD9ccDbQA44HTgLcBr4brFgGLzOz1mH47gIEJYm01swFp7yVL5MV3aPsgziVdCpwLDAXeB/QFfmhmk2LqF8q5pOOBG4CLgKOBLcBSQGb2YjX3nBfyHKcicLjKX61O9Iyp2AQIuBboBawG7gF24so5DhgLXA6MrEG2S4AjgJ9E+r0e+AqwDfhZEPgtwOnAaGB5uHQi8J1wvh14FjgWmAB8D7hY0kQzi5updwJtMcd313AfmaAAviGe8+vxiWw38Bzwzgr1C+Nc0kk4B8cA9wNPAO8HPgdcJOkcM3uhgqy5oKBxyg2Hs/xpdOKgCQ1YCEwFHgeuMLO1kcaPBGYD76hRvhbgBWBVWZsT8cnsYWCCme2K9PmGsp8bgQ8Dy8qtAklfBB4FPoI/aPfG9L3DzObVKHcsJE3FrZQxZvbLGprIm2+I4Rz4PD6RPYVbau0V6hfJ+QJccWeZ2e1lfX0zyHwjMCNFe1mMERQzTnnicJa/ap3oNKFJmo3f9F+As6ITC4CZ7QFuDASkgqR+wHnA3Wa2Nxw7Avg68DJOdFyf/yn7vjKubTP7h6Q7w82NJv7h6lbIm+/Qx0Gch3bby66p2EZRnEsajFsKHcAd0e6ATwGTJV1tZi/V01dKuXIfpzxxOMufVid6llU8DlfM14DL4m66U0tOQFqMx83d+8qOnQ2ciJu/L0oaD5wKvAI8ama/TdF+aeJ7LeH8GyVNwn0HLwHrgFXlD3pRKIhviOc8S2TJ+XmhfDDqkzOzXZJ+gyv3WcAv6pa8ChQ4TrngcJeflDpRbqG1Ar1xB+9jOQnXgiv1Q2XHhoVyK/Bn3Om8H5JWAZea2T8rNSypJ/Dx8PPnCZcNAO6KHNskaZqZPdK1+Jmilfz5hnjOM0EOnJ8Syo0Jbf0VV96TKWhCo7hxygutFCi/pFY80FAt1pjZ0grnU+lE+YTWHMqo8mUCSb3xCMUKM3ul7NQxoZwBbALOB36POypvBS4EfowvaSrhZtyyW25mD8ScXwT8CtgA7AIGA5/FTdYVkkZE/Qo5ozmUufANFTnPCllz3i+UOxP6Kx3vX6fcadAcytzGKWc0h7Io+VtJjmzHYQkerUxCKp3oCSDpKHyG24dPJlVB0ijgC8AZwHHARDO7J+HyccBRRKKbQI9QNuGWWEnBN0hqwWfmc4Pyxy4/Jc0CrsajH5PjrjGzqKPoMWCGpN2h7jzcmklEF2kI7TG+qCVmNjWmnZr4DnXn4g74U4B/A78D5ia8fZM4rxtFcR5BUyjjc43IboxCW/WM00zg08CgcGgD8FUzW9ZFvQ7STQixqTahrXrkT5JjgZnNTKpnZoPS9JMBOulEyUJ7ayj/lTIHpQ+ev7KIrh3CLXjuUnRASzkkz0QtJDPbI+kBYDoepj1oQguK8y08ejPWzLankB/gTvzhGlXFtW0cbB0MxdMiluCOy3KsSWinVr7BLdUFwB/wwbwBeFjSu2PuPYnzupAj56W3bT/i8abIdXFoI5sxgvrG6TlgDr4kagKmAEslnWFm6yrUexr3H1eLzRXO1SP/MA4YG+CW+EP4aqlIpNKJ0oRWihgdKalHtU5yM1sBrIDKkTJJPYAPASvNLKqMT4ZyR0L10oR3UPQlrNdvw9/8Y83s+WrkjqBUp09XF5pZW4wMU/GHZXGKlICa+A4yXBjpfzI+mOcAPy07XonzmpEz5yVdODmh3pBQJvlTshwjqG+c7o8cuk7SVcAIPDCSVG9sCvm6Qj3yd/JZS5qDT7YVfc05+NBS6cQRAEEpO/Bo2OhKvYc0i7QYhWf3xi19VuERmCGSesWcPzWUHRE5rsUfrDV4flEtDxa4ggE8U2P91MiY7774OEatpEqc14QCOC+lkoyL3rekvvikvQdfZueOrMZJUg9Jl+PL/9UZilgRGcrfC5gELExIni5HK55OUe2nuYv2UulEeVCgLXwWSLrEzJ6IVG7CHczTgMu6ECKKCcDreJZvJ5jZNkk/Aj4GfBnPYC/1eQEeFNhJWRRN0pfwpdafgHFdLXkkvQfYEr1O0kDg2+HnD1LeU71oIxu+2/AJJvqQJ3JeC4rg3MyelvQg7vubCdxeXhW36L5bZA4adYyTpNNwN0lvPCjSbGbrC5C5HG3Ur2fNuNW1uKvOsvahpdWJ8gltPvBefJ/X+tDIRjzP6ATgA8DxwN01yNUMrDazrQnnZwPDcbN8FJ59PhD3Ae0FrjSzHQCSpuAP1l48gjYrZrnbYWaLy35PBOZIascjqbuAk/Acrd74tqpv1HBf9aBuviXdgltiI2OWE81U4FxSMwfejqU9lSMkLQ7ft5nZNeHaIjn/DG7FzJc0Fk8GHQ6Mwfm5Lu5+ckQ94/Qk7r/rj++m+L6k0QWnf2TxXE/HI+WV/HV5omqd2D+hBVNyuqR7gStxJ/wF+J67LfibZhm+z7JqSBqGE3Zb0jVm9ryk4bh11oInye0K/d1kZuXWx4mh7IGbt3F4hM5vk3Y8Kng6vtzpg/vsfo2Hs++qwpTOFPXyLelWfBkwxsyeipzrknP8QZsSOTY4fAD+BlwTvhfGeXgjn8mBjcgfxPmYj29EThuAqAv1jJOZvYpvLQP4YxiXVuCT+Uu+X4Z69Wwgnko1oRCBY5BGJ5qS/m0jLSTtIyZtQ9LXgLnAYDPblEln/+eQNB/4KD6ZPR5zvsF5N4SklcDmpDSL7ghJ8/D0kxPMLGk3SLdB3Ob0qhHyXMo3sw6SNBTYbmbPhmMtwNrGg5UNJC3ALbNmYLuk0nJxt5mV/sGiwfkhhqSbccvn73jg5grcMT/+EIqVCsEJPw3P1ev2kxnUOaEBZ9L5XxpuCeUSfDMsZvauOvtooDOuCmV064/wRNUG590DA/CgxwA8qLUOuDhhR0V3xfn4HtyFh1qQapHZkrOBBhpo4FCjlpyyBhpooIFuicaE1kADDfzP4L+nwvob2OmQoQAAAABJRU5ErkJggg==", - "text/latex": [ - "$\\displaystyle \\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0$" - ], - "text/plain": [ - "C₁ C₂ \n", - "─── + ──── + C₃ - C₇ = 0\n", - "625 1250 " - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAKoAAAAUCAYAAAAdtqaHAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAABSNJREFUeJztmmuoVUUYhp9jIpmaQhQIWqFp5A8pMjO8kB5QqR8eioxCRbtAqdjJbmjY62cYgVhHJSG01CQhsDAKBFG0sDQkVDKzk5L4I8HQsjSVLrsf36zarc5ae+3bWUfYDyxm77m+s5j5Zuab1VQoFEjCzKYD74S/j0tam5J3CPAIMBG4HugH/AwcBD4A1kv6LbGxnGnozwczGwAsASYD1wAngS2ASfopytc9pYKBwCrgHNA7JV8TYMALQA/gc2AzcBa4AX9xzcBDwNgq+lQXGvrzw8wG43qvAz4EjgAjgaeAyWY2WtJpSBioofPrgNP4bHw2pb23gZnAYeBhSQdjdfUE5gM3Vd6lZMxsZtA6XtKuCqrIVX8NuJz1r8YH6TxJq6JIM3sNeBpYCjwB0C2hgnnABGAWcD6pFTObj7+kb4BR8ZcEIOmCpKXA7Ep6Uk8a+vPDzAbh1v448EYsWfi4m25mvaCDgWpmtwCvAiskfZrSUH98xP8BTJX0a5owSReyd6P+NPTnzoQQbpP0V3FC6MtnwFXAKIgt/WbWHdgInAAWlmioFbgSWCfpUNWyO59WGvozY2at+AEtKwckbUlJvzmE7Qnp3+EWdyiwI75HfQm4DRiTYQa2hHBjiXxdlZYQNvRnoxU/nGVlA356T6JvCM8mpEfx/aDIoprZSNyKLpe0J02BmfXGR3oB+KKU4ljZccBzwO1Af+ABSZszlj1O8svaaWbxuA2SZnZQTzX6FwD34RbhErAXWJDFqpXQ3xHvSprWQT0V6w/l++Pbu3twj85RYK6kT5LKSLqx3HaqpCmEBQgDtWjJbwcWZajk2hD+UoFvrhfu21sHvF9m2Tb+v/zcCkzBZ/DxWNqBhHqq0X83flrdh7/MJcB2Mxsm6UyJsseAi2W09UNCfMX6zawfvv/bDdwL/AgMAk6VU08NiCxm34T0q4vzRRY1mqEAFzuwTABrzGwNsAJ4JcT1NLMrJP2ZVZ2krcBWgIR20sq2xeOCe2oK7tDelbGqyJNRif5Jsfan4y9zNPBRibLNWdspQcX6geeBk5JmFMV9X6pQHfao34ZwaEL6kBC2w7+n/kvAWwnP/pBnd/i/R9Ip3Hr1wC1MImaW5ALLjRrr74O/x1LWtGZUqb8F2Gtmm8zslJkdMLO5wXeeRivuNsr6tJSob2cIJ8Y1mlkffOJfwLdWblHDwemxhI4uxg9YG2JXqG3hWW1mUyQdiZVrwq/FZgFTS4jOgzZqo78N32LsrYfIEu22Ub7+QcAc4HVgEr51WoXvBeP+zH+o9R5V0jEz24af7OcEDRGGbxHflHQeUq5QM7ASGI7fL38VGm0HfgcGAmOAAcCmKtqoJ1XrN7NlwDhgbJnLby2oVH834EtJC8L//WY2FB8siQO1TszGr1BXmlkzfnFxJzAe78uLUcaKl2VJBUmP4hvyj/GZOQe3zMOBPfitybxK26gn1eo3s+XADKBZ0tFOkPwfqtB/Evg6FneY8rwRNUHSMWAEsB4foM8Ag/FJeFd0zw/QlPb1VL0xswJluKe6Cma2EngQ/77gcN56ysHMNgEDJY0tinsZuF/SsPyUpdPpAzX4AKMPJPbjPtXtwBlJJzpVTAWY2WpgGn5YKB6k5ySdy0VUGZjZHfhyuxh4Dz9/rAUWSurspT8zeZzIR+ADNPImLAu/l+SgpRKexE/6O/BlNHrSvjDrMkjah0+yqcAh/HuBRbhvuMuS69LfoEFWupyPs0GDjmgM1AaXBX8Db+ZLKnj2sa4AAAAASUVORK5CYII=", - "text/latex": [ - "$\\displaystyle 4 C_{1} + C_{2} - C_{6} = 0$" - ], - "text/plain": [ - "4⋅C₁ + C₂ - C₆ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "image/png": "iVBORw0KGgoAAAANSUhEUgAAAG4AAAAUCAYAAABs4ghcAAAAOnRFWHRTb2Z0d2FyZQBNYXRwbG90bGliIHZlcnNpb24zLjEwLjMsIGh0dHBzOi8vbWF0cGxvdGxpYi5vcmcvZiW1igAAAAlwSFlzAAASdAAAEnQB3mYfeAAAA6lJREFUeJztmV2IVGUYx3+zimhWCn0KWy2adhGEkNSGFdWCCF20NwlFQqUXYmCTEhEh//6CV4aIktFNukR5Y2EUCEJ4VZt3iWF+sPkBfZOlohukTRfve7az48x4zsyZ2R3YHxwezvv5f+Y57+eUKpUKtbC9EHgZWAbcDcwF/gIOA58CuyVdrll5gulW7bZ7gU3AcuAW4GdgH2BJf6bLTq9RuQQYeAOYAXwN7AXOA/cQfowB4DngsXY50Qxdrn0BQe/twGfAMeAh4FVgue2lkv5Iyl8TOOAD4EXgKPC8pMNVHcwC1gP3tsOBFulm7TsJQVsnaUeSaHsr8BqwGViTpPeka9peT3D8e6C/2nEASaOSNgNr26G+Wbpc+3zCbHAaeLcqW8AlYKXt2UliT6ryPEJUrwArJF1s1Jmk0WJkt043a488Fe0BSf+mM6IvXwE3AP1JenqqLAMzgV2SvmuvzsIp02HttsuETU9WvpW0r07efdGeqJN/kjAiFwFfwvjADUb7YQ4xk4XBaDupvUzY8GRliLBDrMWcaM/XyU/S5yYJ0wFs30iIZgU4lFWJ7ceB14EHgXnAs5L2Zqx7mnyOfyTphRrtNKU91n2bsIak+VXSnderK6kvT18tUop27OyWjLjbor2Q83wzm3A22gV8klPMCPB3jvI/1UlvVnvCceCJ1PvVJtpolWREzamTf3NVubHAXYp2lu1pkjKJl7Qf2A9gO5dSSQO5KtSnKe0prkj6JW+nBa9xx6NdVCd/YbRja2APgKTfCFvRGYz/+q7Bdk+j/E5TgPb5tn+0fcr2Htt9GbsuE6bZrM9gg7YORrusWqPtm4ClwCjwTZKe3pxsi89O289IOlbVQIlwFfMSsCKjc51iG81pPwSsJHzJdwBvAsO275d0rlGHRa5xkkZsHyDsHF8BdqSyTViS3peUzC7jArcdeIBwx3ckNnQC+Ae4C3gU6AU+LkpwgTSlPU71CUdsDwM/EA7yW9svexxrCVde220PEC4SHgaeJPjyVrrw2LCUVJG0Cnga+AJYTIj+asKPMkxwaF27PchLUdrjF32U/9eUjiFpBFgC7CYEbAOwgPBRPpK+pwQo1ft3IC+2K+Q4DkxGbM8ETgHvSdo00XoaUeuSOTPxDJW+sO2zvRg4J+lsK213AtvvAJ8DZwkXvBsJ68nQROrKQkuBIwztg6n3LdEOEaamyU4vsAe4FfidsGvrl3RmQlVloLCpcorOMqnOZFNkZypwXcp/TvVqwtp6oi4AAAAASUVORK5CYII=", - "text/latex": [ - "$\\displaystyle C_{1} - C_{5} = 0$" - ], - "text/plain": [ - "C₁ - C₅ = 0" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C_{4} = 0\n", - "C_{2} = 0\n", - "- \\frac{4 C_{5}}{1875} - \\frac{C_{6}}{625} - 4 C_{7} + C_{8} + \\frac{8}{375} = 0\n", - "\\frac{C_{5}}{625} + \\frac{C_{6}}{1250} + C_{7} - \\frac{8}{375} = 0\n", - "- \\frac{4 C_{1}}{1875} - \\frac{C_{2}}{625} - 4 C_{3} + C_{4} - C_{8} = 0\n", - "\\frac{C_{1}}{625} + \\frac{C_{2}}{1250} + C_{3} - C_{7} = 0\n", - "4 C_{1} + C_{2} - C_{6} = 0\n", - "C_{1} - C_{5} = 0\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "import numpy as np\n", - "import matplotlib.pyplot as plt\n", - "\n", - "%config InlineBackend.figure_formats = ['svg']\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = 10 \n", - "EI = 5000 \n", - "EA = 20000 \n", - "F = 150\n", - "\n", - "q_AC = 0\n", - "q_BC = q\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", - "\n", - "V_AC = -sym.integrate(q_AC, x) + C_1\n", - "M_AC = sym.integrate(V_AC, x) + C_2\n", - "kappa_AC = M_AC/EI\n", - "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", - "w_AC = -sym.integrate(phi_AC, x) + C_4\n", - "\n", - "V_BC = -sym.integrate(q_BC, x) + C_5\n", - "M_BC = sym.integrate(V_BC, x) + C_6\n", - "kappa_BC = M_BC/EI\n", - "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", - "w_BC = -sym.integrate(phi_BC, x) + C_8\n", - "\n", - "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", - "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", - "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", - "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", - "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", - "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", - "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", - "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", - "\n", - "display(eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8)\n", - "for eq in [eq1, eq2, eq3, eq4, eq5, eq6, eq7, eq8]:\n", - " print(sym.latex(eq))\n" - ] - }, - { - "cell_type": "code", - "execution_count": 3, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle \\left\\{ C_{1} : \\frac{35}{8}, \\ C_{2} : 0, \\ C_{3} : - \\frac{1}{150}, \\ C_{4} : 0, \\ C_{5} : \\frac{35}{8}, \\ C_{6} : \\frac{35}{2}, \\ C_{7} : \\frac{1}{3000}, \\ C_{8} : \\frac{13}{750}\\right\\}$" - ], - "text/plain": [ - "⎧ 13 ⎫\n", - "⎨C₁: 35/8, C₂: 0, C₃: -1/150, C₄: 0, C₅: 35/8, C₆: 35/2, C₇: 1/3000, C₈: ───⎬\n", - "⎩ 750⎭" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/plain": [ - "'\\\\left\\\\{ C_{1} : \\\\frac{35}{8}, \\\\ C_{2} : 0, \\\\ C_{3} : - \\\\frac{1}{150}, \\\\ C_{4} : 0, \\\\ C_{5} : \\\\frac{35}{8}, \\\\ C_{6} : \\\\frac{35}{2}, \\\\ C_{7} : \\\\frac{1}{3000}, \\\\ C_{8} : \\\\frac{13}{750}\\\\right\\\\}'" - ] - }, - "execution_count": 3, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "display(sol)\n", - "sym.latex(sol)" - ] - }, - { - "cell_type": "code", - "execution_count": 14, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - " \n", - " \n", - " \n", - " \n", - " 2025-07-02T16:58:07.214407\n", - " image/svg+xml\n", - " \n", - " \n", - " Matplotlib v3.10.3, https://matplotlib.org/\n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - " \n", - "\n" - ], - "text/plain": [ - "
" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "M_numpy = sym.lambdify(x, M_BC.subs(sol))\n", - "x_numpy = np.linspace(0,4,100)\n", - "plt.figure()\n", - "plt.plot(x_numpy,M_numpy(x_numpy))\n", - "plt.xlabel('$x$')\n", - "plt.ylabel('$M_{BC}$');\n", - "ax = plt.gca()\n", - "ax.spines['right'].set_color('none')\n", - "ax.spines['top'].set_color('none')\n", - "ax.spines['bottom'].set_position('zero')\n", - "ax.spines['left'].set_position('zero')\n", - "ax.invert_yaxis()" - ] - }, - { - "cell_type": "code", - "execution_count": 5, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\\left[\\begin{matrix}0 & 0 & 0 & 1 & 0 & 0 & 0 & 0\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & - \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1\\\\0 & 0 & 0 & 0 & \\frac{1}{625} & \\frac{1}{1250} & 1 & 0\\\\- \\frac{4}{1875} & - \\frac{1}{625} & -4 & 1 & 0 & 0 & 0 & -1\\\\\\frac{1}{625} & \\frac{1}{1250} & 1 & 0 & 0 & 0 & -1 & 0\\\\4 & 1 & 0 & 0 & 0 & -1 & 0 & 0\\\\1 & 0 & 0 & 0 & -1 & 0 & 0 & 0\\end{matrix}\\right]\n", - "\\left[\\begin{matrix}0\\\\0\\\\- \\frac{8}{375}\\\\\\frac{8}{375}\\\\0\\\\0\\\\0\\\\0\\end{matrix}\\right]\n" - ] - } - ], - "source": [ - "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8), (C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "print(sym.latex(A))\n", - "print(sym.latex(b))\n", - "A = np.array(A).astype(np.float64)\n", - "b = np.array(b).astype(np.float64)\n" - ] - }, - { - "cell_type": "code", - "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "A = np.array([\n", - " [0, 0, 0, 1, 0, 0, 0, 0],\n", - " [0, 1, 0, 0, 0, 0, 0, 0],\n", - " [0, 0, 0, 0, -4/1875, -1/625, -4, 1],\n", - " [0, 0, 0, 0, 1/625, 1/1250, 1, 0],\n", - " [-4/1875, -1/625, -4, 1, 0, 0, 0, -1],\n", - " [1/625, 1/1250, 1, 0, 0, 0, -1, 0],\n", - " [4, 1, 0, 0, 0, -1, 0, 0],\n", - " [1, 0, 0, 0, -1, 0, 0, 0]\n", - "])\n", - "\n", - "b = np.array([\n", - " 0,\n", - " 0,\n", - " -8/375,\n", - " 8/375,\n", - " 0,\n", - " 0,\n", - " 0,\n", - " 0\n", - "])" - ] - }, - { - "cell_type": "code", - "execution_count": 6, - "metadata": {}, - "outputs": [ - { - "data": { - "text/plain": [ - "array([[ 4.37500000e+00],\n", - " [ 0.00000000e+00],\n", - " [-6.66666667e-03],\n", - " [ 0.00000000e+00],\n", - " [ 4.37500000e+00],\n", - " [ 1.75000000e+01],\n", - " [ 3.33333333e-04],\n", - " [ 1.73333333e-02]])" - ] - }, - "execution_count": 6, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.linalg.solve(A,b)" - ] - }, - { - "cell_type": "code", - "execution_count": 7, - "metadata": { - "tags": [ - "remove-cell" - ] - }, - "outputs": [ - { - "data": { - "text/latex": [ - "$\\displaystyle \\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]$" - ], - "text/plain": [ - "Matrix([\n", - "[ 4.375],\n", - "[ 0],\n", - "[-0.00666666666666667],\n", - "[ 0],\n", - "[ 4.375],\n", - "[ 17.5],\n", - "[0.000333333333333334],\n", - "[ 0.0173333333333333]])" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "\\left[\\begin{matrix}4.375\\\\0\\\\-0.00666666666666667\\\\0\\\\4.375\\\\17.5\\\\0.000333333333333334\\\\0.0173333333333333\\end{matrix}\\right]\n", - "\\left[\\begin{matrix}1 & 0 & 0 & 0 & 0 & 0 & 0 & 0 & 4.375\\\\0 & 1 & 0 & 0 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 1 & 0 & 0 & 0 & 0 & 0 & -0.00666666666666667\\\\0 & 0 & 0 & 1 & 0 & 0 & 0 & 0 & 0\\\\0 & 0 & 0 & 0 & 1 & 0 & 0 & 0 & 4.375\\\\0 & 0 & 0 & 0 & 0 & 1 & 0 & 0 & 17.5\\\\0 & 0 & 0 & 0 & 0 & 0 & 1 & 0 & 0.000333333333333334\\\\0 & 0 & 0 & 0 & 0 & 0 & 0 & 1 & 0.0173333333333333\\end{matrix}\\right]\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "\n", - "A_sym = sym.Matrix(A)\n", - "b_sym = sym.Matrix(b)\n", - "\n", - "sol_sym = A_sym.LUsolve(b_sym)\n", - "\n", - "display(sol_sym)\n", - "\n", - "print(sym.latex(sol_sym))\n", - "\n", - "identity = sym.eye(sol_sym.rows)\n", - "rr_augmented = identity.row_join(sol_sym)\n", - "print(sym.latex(rr_augmented))" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": {}, - "outputs": [ - { - "name": "stdout", - "output_type": "stream", - "text": [ - "C_1 = 35/8 ≈ 4.37500000000000\n", - "C_2 = 0 ≈ 0\n", - "C_3 = -1/150 ≈ -0.00666666666666667\n", - "C_4 = 0 ≈ 0\n", - "C_5 = 35/8 ≈ 4.37500000000000\n", - "C_6 = 35/2 ≈ 17.5000000000000\n", - "C_7 = 1/3000 ≈ 0.000333333333333333\n", - "C_8 = 13/750 ≈ 0.0173333333333333\n" - ] - } - ], - "source": [ - "import sympy as sym\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = 10 \n", - "EI = 5000 \n", - "EA = 20000 \n", - "F = 150\n", - "\n", - "q_AC = 0\n", - "q_BC = q\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "C_5, C_6, C_7, C_8 = sym.symbols('C_5 C_6 C_7 C_8')\n", - "\n", - "V_AC = -sym.integrate(q_AC, x) + C_1\n", - "M_AC = sym.integrate(V_AC, x) + C_2\n", - "kappa_AC = M_AC/EI\n", - "phi_AC = sym.integrate(kappa_AC, x) + C_3\n", - "w_AC = -sym.integrate(phi_AC, x) + C_4\n", - "\n", - "V_BC = -sym.integrate(q_BC, x) + C_5\n", - "M_BC = sym.integrate(V_BC, x) + C_6\n", - "kappa_BC = M_BC/EI\n", - "phi_BC = sym.integrate(kappa_BC, x) + C_7\n", - "w_BC = -sym.integrate(phi_BC, x) + C_8\n", - "\n", - "eq1 = sym.Eq(w_AC.subs(x,0),0)\n", - "eq2 = sym.Eq(M_AC.subs(x,0),0)\n", - "eq3 = sym.Eq(w_BC.subs(x,4),0)\n", - "eq4 = sym.Eq(phi_BC.subs(x,4),0)\n", - "eq5 = sym.Eq(w_AC.subs(x,4)-w_BC.subs(x,0),0)\n", - "eq6 = sym.Eq(phi_AC.subs(x,4)-phi_BC.subs(x,0),0)\n", - "eq7 = sym.Eq(M_AC.subs(x,4) - M_BC.subs(x,0),0)\n", - "eq8 = sym.Eq(V_AC.subs(x,4) - V_BC.subs(x,0),0)\n", - "\n", - "sol = sym.solve([eq1,eq2,eq3,eq4,eq5,eq6,eq7,eq8],(C_1,C_2,C_3,C_4,C_5,C_6,C_7,C_8))\n", - "for k, v in sol.items():\n", - " print(f\"{k} = {v} ≈ {v.evalf()}\")" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "::::::{prf:example-end}\n", - "::::::" - ] - }, - { - "cell_type": "markdown", - "metadata": {}, - "source": [ - "## Meer voorbeelden\n", - "Het voorbeeld in hoofdstuk 2.7.3 van het boek Mechanica, spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013` richt zich op extensie. Voorbeeld 3b in hoofdstuk 8.2 is een buigingsprobleem.\n", - "\n", - "## Instructies in collegevorm\n", - "\n", - "Dit onderwerp is [in les 3](https://collegerama.tudelft.nl/Mediasite/Channel/public-ceg-ctb2210/watch/757766716fcb45718fca678dadaa396d1d?sortBy=most-recent) gepresenteerd in collegevorm van 0:40:00 tot 0:48:30\n", - "\n", - "## Opgaves\n", - "\n", - "- Opgaves 2.48 en 2.50 in hoofdstuk 2.9 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter2/).\n", - "- Opgaves 8.7, 8.10, 8.11 in hoofdstuk 8.6 van het boek Mechanica: spanningen, vervormingen en verplaatsingen {cite:p}`Hartsuijker2013`, antwoorden zijn beschikbaar op [deze website](https://icozct.tudelft.nl/TUD_CT/boekantwoorden/vol2/Chapter8/)." - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 2 -} diff --git a/book/intro.md b/book/intro.md index fae52ac..165c9f7 100644 --- a/book/intro.md +++ b/book/intro.md @@ -1,210 +1,5 @@ ---- -file_format: mystnb -kernelspec: - name: python3 ---- - # TeachBooks Manual -## Nesting inside of a start-directive - -### Different directives - -::::{prf:theorem-start} The parent theorem -:label: parent-theorem-label - -This is a parent theorem. This text is inside the parent theorem. - -:::{prf:axiom-start} A child axiom -:label: child-axiom-label - -This is a child axiom. This text is inside the child directive. -::: - -This is text after the child-start, before the child-end, inside the parent theorem. - -:::{prf:axiom-end} -Text inside the end. This text will not be rendered. -::: - -Finally some text after the entire child, inside the theorem start. - -:::: - -Some text after the entire parent theorem start, but before the parent theorem end. - -:::{prf:theorem-end} -::: - -### Same directives - -::::{prf:theorem-start} The parent theorem -:label: parent-theorem-label2 - -This is a parent theorem. This text is inside the parent theorem. - -:::{prf:theorem-start} A child theorem -:label: child-theorem-label - -This is a child theorem. This text is inside the child directive. -::: - -This is text after the child-start, before the child-end, inside the parent theorem. - -:::{prf:theorem-end} -::: - -Finally some text after the entire child, inside the theorem start. - -:::: - -Some text after the entire parent theorem start, but before the parent theorem end. - -:::{prf:theorem-end} -::: - -## Nesting outside of a start-directive - -### Different directives - -::::{prf:theorem-start} The parent theorem -:label: parent-theorem-label3 - -This is a parent theorem. This text is inside the parent theorem. -:::: - -Some text outside the parent theorem start, before the child start directive. - -:::{exercise-start} A child exercise -:label: child-exercise-label2 - -This is a child exercise. This text is inside the child directive. -::: - -This is text after the child-start, before the child-end. - -:::{exercise-end} -::: - -Finally some text after the entire child, outside the theorem start, before the end. - -:::{prf:theorem-end} -::: - -:::{exercise} This is an exercise outside any theorem. -:label: exercise-outside-theorem - -Solve this exercise. -::: - -### Same directives - -::::{prf:theorem-start} The parent theorem -:label: parent-theorem-label4 - -This is a parent theorem. This text is inside the parent theorem. -:::: - -Some text outside the parent theorem start, before the child start directive. - -:::{prf:theorem-start} A child theorem -:label: child-theorem-label5 - -This is a child theorem. This text is inside the child directive. -::: - -This is text after the child-start, before the child-end. - -:::{prf:theorem-end} -::: - -Finally some text after the entire child, outside the theorem start, before the end. - -:::{prf:theorem-end} -::: - -### Nested exercise directives - -:::{exercise-start} An outer exercise with an inner exercise between start and end -:label: outer-exercise-label -This is an outer exercise. This text is inside the outer exercise. -::: - -:::{exercise-start} An inner exercise -:label: inner-exercise-label -This is an inner exercise. This text is inside the inner exercise. -::: -This is text after the inner exercise start, before the inner exercise end. - -:::{exercise-end} -::: - -Finally some text after the entire inner exercise, inside the outer exercise. - -:::{exercise-end} -::: - -## Examples with code - -### Block directive with also a figure - -:::{prf:theorem-start} A title -:label: theorem-label - -This is a theorem. This text is inside the directive. -::: - -Now some extra text that should come inside the theorem. - -:::{figure} images/sphinx-logo.svg -:alt: Sphinx logo -:width: 200px -:align: center - -Sphinx is a great tool for documentation! This figure should be inside the theorem. -::: - -```{code-cell} ipython3 -a = "This is some" -b = "Python code" -c = "that should be inside the theorem." -print(f"{a} {b} {c}") -``` - -:::{prf:theorem-end} -::: - -### Figure directive with nothing with also another figure - -:::{figure-start} images/nothing.svg -:name: figure-label -:alt: Nothing -:align: center - -This is a figure. This text is inside the caption. -::: - -```{code-cell} ipython3 -a = "This is some" -b = "Python code" -c = "that should be inside the figure," -d = "above the caption." -print(f"{a} {b} {c} {d}") -``` - -Now some extra text that should come inside the figure, above the caption. - -:::{figure} images/sphinx-logo.svg -:alt: Sphinx logo -:width: 200px -:align: center - -Sphinx is a great tool for documentation! This figure should be inside the parent figure, above the parent caption. -::: - -:::{figure-end} -::: - This manual is primarily designed for and by teachers for use in education, but should be a useful resource for anyone interested in creating and collaborating on a TeachBook: a Jupyter book v1 v1 using the Sphinx logo Sphinx backend with additional editing tools and book features. Our aim is to provide a simple way to start book-making for new users (it only takes 10 clicks!), ease the editing process for anyone, and provide additional features developed for education-purposes. We hope you find this resource useful and refer back to it often. TeachBooks originated at Delft University of Technology in the Netherlands, and some of the material in this Manual reflects tools and resources specific to TU Delft. With the exception of a few tools for which we have educational licenses at Delft, everything in this Manual is open source. Despite this, we are happy to grow our open source community beyond Delft and welcome contributions from all users. diff --git a/book/lesoefeningen.ipynb b/book/lesoefeningen.ipynb deleted file mode 100644 index b6e6d48..0000000 --- a/book/lesoefeningen.ipynb +++ /dev/null @@ -1,316 +0,0 @@ -{ - "cells": [ - { - "cell_type": "markdown", - "id": "44f13f99", - "metadata": {}, - "source": [ - "````{margin}\n", - "```{attributiongrey} Bronvermelding\n", - ":class: attribution\n", - "\n", - "Deze oefening is aangepast van https://oit.tudelft.nl/CT1000/2024/week_3/session_1/intro.html\n", - "\n", - "% Figures from https://github.com/TUDelft-books/CT1000/blob/2025/book/week_3/session_1/intro_data/Tekening1.vsdx\n", - "\n", - "```\n", - "```` \n", - "\n", - "# Begeleide oefening 1_oud\n", - "\n", - "Gegeven is de volgende constructie:\n", - "\n", - "```{figure} lesoefeningen_data/figure2.svg\n", - "---\n", - "align: center\n", - "---\n", - "Constructie, $EI = 6400 \\ \\rm{kNm^2}$\n", - "```\n", - "\n", - ":::::{exercise}\n", - ":label: ODE_3_1\n", - ":nonumber: true\n", - "\n", - "Bepaal de segmenten en teken de vrijlichaamsschema's.\n", - "\n", - "```{h5p} https://tudelft.h5p.com/content/1292630673009985967/embed\n", - "```\n", - "\n", - ":::::\n", - "\n", - "% solution_start\n", - "\n", - "::::{admonition} Solution\n", - ":class: solution, dropdown\n", - "\n", - "$q_z = -12 \\cdot x + 0$\n", - "\n", - "::::\n", - "\n", - "% solution_end\n", - "\n", - ":::::{exercise}\n", - ":label: ODE_3_2\n", - ":nonumber: true\n", - "\n", - "Bepaal de relaties van de krachtsgrootheden en verplaatsingen.\n", - "\n", - "```{h5p} https://tudelft.h5p.com/content/1292630677998825487/embed\n", - "```\n", - "\n", - ":::::\n", - "\n", - "% solution_start\n", - "\n", - "::::{admonition} Solution\n", - ":class: solution, dropdown\n", - "\n", - "- $ w \\left( 0\\right) = 0 $\n", - "- $ M \\left( 0\\right) = 0$\n", - "- $ w \\left( 8\\right) = 0$\n", - "- $ \\varphi \\left( 8 \\right) = 0$\n", - "\n", - "::::\n", - "\n", - "% solution_end\n", - "\n", - ":::::{exercise-start}\n", - ":label: ODE_3_3\n", - ":nonumber: true\n", - "\n", - "Los de integratieconstanten op. Je kan gebruik maken van de volgende code cellen. (Klik op {fa}`rocket` --> {guilabel}`Live Code` om live python code te activeren op deze pagina.)\n", - "\n", - "```{h5p} https://tudelft.h5p.com/content/1292630704310133517/embed\n", - "```\n", - "\n", - ":::::\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "91dc34be", - "metadata": {}, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "A = np.array([\n", - " [ , , , ],\n", - " [ , , , ],\n", - " [ , , , ],\n", - " [ , , , ],\n", - "])\n", - "\n", - "b = np.array([\n", - " ,\n", - " ,\n", - " ,\n", - " ,\n", - "])\n" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "ce6f1792", - "metadata": {}, - "outputs": [], - "source": [ - "np.linalg.solve(A,b)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "9e466305", - "metadata": {}, - "outputs": [], - "source": [ - "import sympy as sym\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = \n", - "EI = 6400\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "\n", - "V = -sym.integrate(q, x) + C_1\n", - "M = sym.integrate(V, x) + C_2\n", - "kappa = M/EI\n", - "phi = sym.integrate(kappa, x) + C_3\n", - "w = -sym.integrate(phi, x) + C_4\n", - "\n", - "eq1 = sym.Eq(w.subs(x,0), )\n", - "eq2 = sym.Eq( .subs(x,0), )\n", - "eq3 = sym.Eq(w.subs(x,8), )\n", - "eq4 = sym.Eq( .subs(x,8),0)\n", - "\n", - "sol = sym.solve([eq1,eq2,eq3,eq4],(C_1,C_2,C_3,C_4))\n", - "for k, v in sol.items():\n", - " print(f\"{k} = {v} ≈ {v.evalf()}\")" - ] - }, - { - "cell_type": "markdown", - "id": "94270e4f", - "metadata": {}, - "source": [ - ":::::{exercise-end}\n", - ":::::\n", - "\n", - "% solution_start\n", - "\n", - "::::{solution-start} ODE_3_3\n", - ":class: dropdown\n", - "::::\n", - "\n", - "$ w = - \\cfrac{x^{5}}{10} + \\cfrac{64 x^{3}}{5} - \\cfrac{2048 x}{5} = - 0.1 x^{5} + 12.8 x^{3} - 409.6 x $\n", - "% solution_end" - ] - }, - { - "cell_type": "code", - "execution_count": 22, - "id": "f46e4efd", - "metadata": { - "tags": [ - "solution" - ] - }, - "outputs": [], - "source": [ - "import numpy as np\n", - "\n", - "A = np.array([\n", - " [ 0 , 0 , 0,1],\n", - " [ 0 , 1 , 0,0],\n", - " [-1/75 ,-1/200,-8,1],\n", - " [ 1/200, 1/800, 1,0],\n", - "])\n", - "\n", - "b = np.array([\n", - " 0,\n", - " 0,\n", - " 64/125,\n", - " -8/25,\n", - "])\n" - ] - }, - { - "cell_type": "code", - "execution_count": 25, - "id": "300cd851", - "metadata": { - "tags": [ - "solution" - ] - }, - "outputs": [ - { - "data": { - "text/plain": [ - "array([-7.68e+01, 0.00e+00, 6.40e-02, 0.00e+00])" - ] - }, - "execution_count": 25, - "metadata": {}, - "output_type": "execute_result" - } - ], - "source": [ - "np.linalg.solve(A,b)" - ] - }, - { - "cell_type": "code", - "execution_count": 28, - "id": "96516034", - "metadata": { - "tags": [ - "solution" - ] - }, - "outputs": [ - { - "data": { - "image/png": "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", - "text/latex": [ - "$\\displaystyle - 0.1 x^{5} + 12.8 x^{3} - 409.6 x$" - ], - "text/plain": [ - " 5 3 \n", - "- 0.1⋅x + 12.8⋅x - 409.6⋅x" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], - "source": [ - "import sympy as sym\n", - "sym.init_printing()\n", - "\n", - "x = sym.symbols('x')\n", - "\n", - "q = -12 * x\n", - "EI = 6400\n", - "\n", - "C_1, C_2, C_3, C_4 = sym.symbols('C_1 C_2 C_3 C_4')\n", - "\n", - "V = -sym.integrate(q, x) + C_1\n", - "M = sym.integrate(V, x) + C_2\n", - "kappa = M/EI\n", - "phi = sym.integrate(kappa, x) + C_3\n", - "w = -sym.integrate(phi, x) + C_4\n", - "\n", - "eq1 = sym.Eq(w.subs(x,0),0)\n", - "eq2 = sym.Eq(M.subs(x,0),0)\n", - "eq3 = sym.Eq(w.subs(x,8),0)\n", - "eq4 = sym.Eq(phi.subs(x,8),0)\n", - "\n", - "sol = sym.solve([eq1,eq2,eq3,eq4],(C_1,C_2,C_3,C_4))\n", - "\n", - "display((w.subs(sol)*EI).evalf())\n", - "\n", - "A,b = sym.linear_eq_to_matrix((eq1,eq2,eq3,eq4), (C_1,C_2,C_3,C_4))" - ] - }, - { - "cell_type": "markdown", - "id": "b3a4c9d4", - "metadata": {}, - "source": [ - "% solution_start\n", - "\n", - "::::{solution-end}\n", - "::::\n", - "\n", - "% solution_end" - ] - } - ], - "metadata": { - "kernelspec": { - "display_name": "base", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.12.11" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 68cf7ca75696d9fe5fdca54f9886094d392baf71 Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 17:41:29 +0100 Subject: [PATCH 22/27] Add extension to remove YAML front matter from includes Introduced a custom Sphinx extension 'include_remove_yaml' to strip YAML front matter from included files. Updated _config.yml to register the extension, and added YAML front matter to intro.md for demonstration. --- book/_config.yml | 2 ++ book/_ext/include_remove_yaml/__init__.py | 33 +++++++++++++++++++++++ book/intro.md | 5 ++++ 3 files changed, 40 insertions(+) create mode 100644 book/_ext/include_remove_yaml/__init__.py diff --git a/book/_config.yml b/book/_config.yml index 02e26d1..99ea80d 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -11,6 +11,8 @@ html: baseurl : "https://teachbooks.io/manual/" sphinx: + local_extensions: + include_remove_yaml: _ext/ extra_extensions: - sphinx_inline_tabs - sphinx.ext.imgconverter diff --git a/book/_ext/include_remove_yaml/__init__.py b/book/_ext/include_remove_yaml/__init__.py new file mode 100644 index 0000000..a6181f5 --- /dev/null +++ b/book/_ext/include_remove_yaml/__init__.py @@ -0,0 +1,33 @@ +from __future__ import annotations +from sphinx.util import logging +logger = logging.getLogger(__name__) + +def _strip_yaml(lines: list[str]) -> list[str]: + + if not lines: + return lines + for ix,lines_set in enumerate(lines): + new_lines = lines_set.splitlines() + if new_lines[0].strip() != "---": + continue + # yaml front matter detected + # find closing delimiter + index_end = 1 + for i in range(1, len(new_lines)): + tok = new_lines[i].strip() + if tok == "---": + index_end = i + break + new_lines = new_lines[index_end+1 :] + lines[ix] = "\n".join(new_lines) + return lines # no closing delimiter -> leave unchanged + +def on_include_read(app, docname, path, content): + # content is a list[str]; modify in-place + new = _strip_yaml(content) + if new is not content: + content[:] = new + +def setup(app): + app.connect("include-read", on_include_read) + return {} diff --git a/book/intro.md b/book/intro.md index 165c9f7..c2739e3 100644 --- a/book/intro.md +++ b/book/intro.md @@ -1,3 +1,8 @@ +--- +file_format: mystnb +kernelspec: + name: python3 +--- # TeachBooks Manual This manual is primarily designed for and by teachers for use in education, but should be a useful resource for anyone interested in creating and collaborating on a TeachBook: a Jupyter book v1 v1 using the Sphinx logo Sphinx backend with additional editing tools and book features. Our aim is to provide a simple way to start book-making for new users (it only takes 10 clicks!), ease the editing process for anyone, and provide additional features developed for education-purposes. We hope you find this resource useful and refer back to it often. From ee8777516e7a6b1e8cd8cd5252fc0c92d275779f Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 17:53:12 +0100 Subject: [PATCH 23/27] Remove custom YAML extension and update requirements Deleted the custom 'include_remove_yaml' Sphinx extension and its configuration. Cleaned up YAML front matter from intro.md. Updated requirements.txt to use released teachbooks package, added sphinx-gated-directives, and removed duplicate or outdated dependencies. --- book/_config.yml | 2 -- book/_ext/include_remove_yaml/__init__.py | 33 ----------------------- book/intro.md | 5 ---- requirements.txt | 10 +++---- 4 files changed, 3 insertions(+), 47 deletions(-) delete mode 100644 book/_ext/include_remove_yaml/__init__.py diff --git a/book/_config.yml b/book/_config.yml index 99ea80d..02e26d1 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -11,8 +11,6 @@ html: baseurl : "https://teachbooks.io/manual/" sphinx: - local_extensions: - include_remove_yaml: _ext/ extra_extensions: - sphinx_inline_tabs - sphinx.ext.imgconverter diff --git a/book/_ext/include_remove_yaml/__init__.py b/book/_ext/include_remove_yaml/__init__.py deleted file mode 100644 index a6181f5..0000000 --- a/book/_ext/include_remove_yaml/__init__.py +++ /dev/null @@ -1,33 +0,0 @@ -from __future__ import annotations -from sphinx.util import logging -logger = logging.getLogger(__name__) - -def _strip_yaml(lines: list[str]) -> list[str]: - - if not lines: - return lines - for ix,lines_set in enumerate(lines): - new_lines = lines_set.splitlines() - if new_lines[0].strip() != "---": - continue - # yaml front matter detected - # find closing delimiter - index_end = 1 - for i in range(1, len(new_lines)): - tok = new_lines[i].strip() - if tok == "---": - index_end = i - break - new_lines = new_lines[index_end+1 :] - lines[ix] = "\n".join(new_lines) - return lines # no closing delimiter -> leave unchanged - -def on_include_read(app, docname, path, content): - # content is a list[str]; modify in-place - new = _strip_yaml(content) - if new is not content: - content[:] = new - -def setup(app): - app.connect("include-read", on_include_read) - return {} diff --git a/book/intro.md b/book/intro.md index c2739e3..165c9f7 100644 --- a/book/intro.md +++ b/book/intro.md @@ -1,8 +1,3 @@ ---- -file_format: mystnb -kernelspec: - name: python3 ---- # TeachBooks Manual This manual is primarily designed for and by teachers for use in education, but should be a useful resource for anyone interested in creating and collaborating on a TeachBook: a Jupyter book v1 v1 using the Sphinx logo Sphinx backend with additional editing tools and book features. Our aim is to provide a simple way to start book-making for new users (it only takes 10 clicks!), ease the editing process for anyone, and provide additional features developed for education-purposes. We hope you find this resource useful and refer back to it often. diff --git a/requirements.txt b/requirements.txt index 946f15a..c7b9259 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,8 +1,8 @@ -# teachbooks == 0.2.3 -git+https://github.com/TeachBooks/TeachBooks@issue-127 +teachbooks == 0.2.3 sphinx-inline-tabs git+https://github.com/TeachBooks/TeachBooks-Favourites +sphinx-gated-directives teachbooks-sphinx-grasple @@ -11,8 +11,4 @@ sphinx-apa-references # For executing Jupyter Notebooks myst_nb numpy -matplotlib - -sphinx-tudelft-theme == 1.2.1 - -git+https://github.com/TeachBooks/Sphinx-Gated-Directives \ No newline at end of file +matplotlib \ No newline at end of file From c7f7917fe2affa846325df2350bf5ddad06409ca Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 17:54:12 +0100 Subject: [PATCH 24/27] Remove sphinx_tudelft_theme from Sphinx extensions The sphinx_tudelft_theme extension has been removed from the list of Sphinx extensions in the configuration file, possibly because it is no longer needed or has been replaced. --- book/_config.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/book/_config.yml b/book/_config.yml index 02e26d1..9a6affb 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -17,7 +17,6 @@ sphinx: - teachbooks_favourites - teachbooks_sphinx_grasple - sphinx_apa_references - - sphinx_tudelft_theme - sphinx_gated_directives config: sphinx_gated_directives: From 46d70c1578bbf21ddb40bdcacc978d909c14135d Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 17:56:08 +0100 Subject: [PATCH 25/27] Remove unused Sphinx config options Cleaned up book/_config.yml by removing unused or unnecessary Sphinx configuration options related to sphinx_gated_directives and sphinx_codex. This helps simplify the configuration file. --- book/_config.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/book/_config.yml b/book/_config.yml index 9a6affb..82c2340 100644 --- a/book/_config.yml +++ b/book/_config.yml @@ -19,8 +19,6 @@ sphinx: - sphinx_apa_references - sphinx_gated_directives config: - sphinx_gated_directives: - override_existing: ['exercise'] language: en html_js_files: - https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.4/require.min.js @@ -55,8 +53,6 @@ sphinx: summaries: false individual: false nb_execution_includepatterns: "**/MANUAL.ipynb" - sphinx_codex_name: "Voorbeeld" - sphinx_codex_merge_with_proof: true # default value teachbooks: attribution_location: margin From aacc7368e2eda56d1160da6b7a5849439cc11bec Mon Sep 17 00:00:00 2001 From: Dennis den Ouden-van der Horst Date: Sun, 4 Jan 2026 18:03:01 +0100 Subject: [PATCH 26/27] Update teachbooks dependency source Switched teachbooks dependency to use a specific Git branch (issue-127) instead of the released version. This allows for testing or using unreleased changes from the repository. --- requirements.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index c7b9259..239e2ff 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,5 @@ -teachbooks == 0.2.3 +# teachbooks == 0.2.3 +git+https://github.com/TeachBooks/TeachBooks@issue-127 sphinx-inline-tabs git+https://github.com/TeachBooks/TeachBooks-Favourites From 684b9c84a4dceb90d51e6bf70d65679c24cf48c3 Mon Sep 17 00:00:00 2001 From: Tom van Woudenberg Date: Mon, 5 Jan 2026 09:15:20 +0100 Subject: [PATCH 27/27] Fix matplotlib entry in requirements.txt --- requirements.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements.txt b/requirements.txt index 239e2ff..a244d27 100644 --- a/requirements.txt +++ b/requirements.txt @@ -12,4 +12,4 @@ sphinx-apa-references # For executing Jupyter Notebooks myst_nb numpy -matplotlib \ No newline at end of file +matplotlib