Example 6: User-drawn trib areas#

The notebook and its PDF are in Examples/Example 6 in the repository.

%load_ext autoreload
%autoreload 2
from decimal import Decimal
from papermodels.paper.pdf import load_pdf_annotations
from papermodels.datatypes.geometry_graph import GeometryGraph
from papermodels.datatypes.joist_models import JoistArrayModel
%matplotlib inline

1. Create the graph#

graph = GeometryGraph.from_pdf_file("user-applied-trib-area-arbitrary-loads.pdf", scale=Decimal(1 / 72 * 4))

# IMPORTANT!!!
# When you are working with your own trib areas and the element does NOT have any joists or collector elements transferring load
# to it then you have to be sure NOT to assign a collector behaviour (as in this example).
# If your trib element does not have any other collectors loading it then it becomes a collector element itself
# because it has no precedents in the load graph. If you assign a collector behaviour then the trib area you assigned
# will be over-written by the new collector trib area that is generated for it.
# graph.assign_collector_behaviour(JoistArrayModel, spacing=1.0)

les = graph.create_loaded_elements()
graph.plot_annotations(0)
../../_images/7bb50bcda262fbf8246de96ad0b9118af05f0aeb69d53f980eeffb25f225bc01.png
graph.plot_connectivity()
../../_images/08735b13ef1a17119053c481848690bfd4d9fdf72bfb003c2507e9c936ded637.png

Breaking change!#

LoadedElement.model(precision=3) is now a method instead of an attribute (property). So you have to call .model(), not .model.

The new method allows for a user-defined amount of precision in the numbers. Default is 3 decimal places.

# See how the weirdly shaped loads get turned into distributed loads?
les['FB0.0'].model()
{'element_attributes': {'tag': 'FB0.0',
  'length': 13.788,
  'orientation': 'horizontal',
  'vert_correspondent_below': [],
  'vert_correspondent_above': [],
  'horz_intersects_above': [],
  'horz_intersects_below': ['CT0.0', 'CT0.1', 'CT0.2']},
 'element_geometry': {'geometry': 'LINESTRING (11.319 20.64, 25.107 20.64)',
  'supports': [{'location': 0.29, 'fixity': 'pin'},
   {'location': 4.504, 'fixity': 'roller'},
   {'location': 11.254, 'fixity': 'roller'}]},
 'loads': {'point_loads': [],
  'distributed_loads': [{'transfer_source': '',
    'transfer_reaction_index': '',
    'occupancy': 'f',
    'load_components': [],
    'applied_area': 20.036,
    'start_loc': 0.191,
    'start_magnitude': 0.158,
    'end_loc': 6.524,
    'end_magnitude': 0.158},
   {'transfer_source': '',
    'transfer_reaction_index': '',
    'occupancy': 'a',
    'load_components': [],
    'applied_area': 1.027,
    'start_loc': 8.62,
    'start_magnitude': 0.0,
    'end_loc': 9.343,
    'end_magnitude': 0.377},
   {'transfer_source': '',
    'transfer_reaction_index': '',
    'occupancy': 'a',
    'load_components': [],
    'applied_area': 5.338,
    'start_loc': 9.343,
    'start_magnitude': 0.377,
    'end_loc': 11.237,
    'end_magnitude': 0.372},
   {'transfer_source': '',
    'transfer_reaction_index': '',
    'occupancy': 'a',
    'load_components': [],
    'applied_area': 1.162,
    'start_loc': 11.237,
    'start_magnitude': 0.372,
    'end_loc': 12.067,
    'end_magnitude': 0.0}]}}

2. Output the model files#

Each LoadedElement is written to a JSON file, which can be read back into a dictionary with the standard library json module.

import pathlib
output_dir = pathlib.Path("model_files") / "json"
output_dir.mkdir(parents=True, exist_ok=True)

for tag, loaded_element in les.items():
    with open(output_dir / f"{tag}.json", "w") as file:
        loaded_element.dump_json(file, precision=4)  # numeric precision of the output