robbievanleeuwen / section-properties

Analysis of an arbitrary cross-section in python using the finite element method.
https://sectionproperties.rtfd.io
MIT License
423 stars 92 forks source link

Overhaul pre-processor to include shapely #21

Closed robbievanleeuwen closed 2 years ago

robbievanleeuwen commented 4 years ago

Is your feature request related to a problem? Please describe. No specific problems, but would increase functionality and simplify a fair bit of the code.

Describe the solution you'd like Use shapely in all it's glory.

Describe alternatives you've considered Writing the code yourself - tried that, no fun.

Additional context Suggesting in issue #9 by Agent6-6-6.

connorferster commented 3 years ago

@Agent6-6-6

Thanks for the reminder on the compound perimeter. It is something that I became aware of a couple months ago and, at the time, did not know how to make work. Now I think I can get a correct value for a compound geometry. Should only take an extra line or two if code now.

Regarding the API change of mesh_size (Geometry) and mesh_sizes (CompoundGeometry), I made that change about a week ago thinking that would be more descriptive and serve as a reminder to the user of the distinction between Geometry vs CompoundGeometry. Reading your comments, however, I am struck by how troublesome it is to have two different argument names. It feels like an obvious choice now to keep the same argument name for both.

I think I would rather make the arguments obvious and consistent vs writing documentation to explain it!

connorferster commented 3 years ago

@Czarified Re: perimeter. I have not used it yet myself. I just think of it as "nice to have" info in case someone ever does need it. Now that it has occurred to me how to do it, we won't have to write the extra documentation! XD

connorferster commented 3 years ago

Geometry.create_mesh() now as arg 'mesh_sizes' instead of 'mesh_size'. CompoundGeometry.calculate_perimeter() now calculates exterior perimeter if the geometry is not disjoint. If disjoint, method returns -1.

Agent6-6-6 commented 3 years ago

@Czarified , @connorferster , the reason I originally requested the perimeter was added was for two reasons

If its possible to return in shapely a merged geometry and fill the holes ignoring any internal holes (get geometry of holes and add as a union as external regions instead?), then you can use boundary.length to get external perimeter. I started going down this route last night but not being so familiar with the shapely and how you've used it for the geometry I wasn't having much success.

(guess I'll see how you solved it 😁👍)

Agent6-6-6 commented 3 years ago

@connorferster, one other subtle change I just noted trying to track down why the time_info = false default wasn't working for me with the Section class that's worth adding to the list of differences for converting scripts:-

Agent6-6-6 commented 3 years ago

Hi @connorferster, seeing some weird behaviour with the following example if you wouldn't mind having a look.

I'm pretty sure this should be working, but I see the shear centre being way off the section when it should lie on an axis of symmetry given the arrangement (it worked fine in v1.08). Figure_1

The code is part of a larger example with a few more geometry elements for welds joining the two elements that I just couldn't get the shapely version to create a mesh for (just did nothing hanging on create_mesh command), so I started removing geometry until I saw the shear centre being out here in this example and thought this might be the reason why my larger example wasn't working as something isn't maybe meshing properly. Could you take a look when you have some time as definitely something weird going on.

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

d = 612  # I section depth
b_f = 229  # I-section flange width
t_f = 19.6  # I-section flange thickness
t_w = 11.9  # I-section web thickness
r_1 = 14  # I-section root radii

d_tee = 202  # strengthening tee depth
b_tee = 178  # strengthening tee flange width
t_f_tee = 12.8  # strenghtening tee flange thickness
t_w_tee = 7.8  # strengthening tee web thickness
r_1_tee = 11.4  # strnegthening tee root radii

n_r = 5  # number of points considered around root radii

mesh_area = 20  # max mesh size

# I-Section beam
geometry1 = sections.i_section(d=d, b=b_f, t_f=t_f, t_w=t_w, r=r_1, n_r=n_r).align_center()

# strengthening tee
geometry2 = (
    sections.tee_section(d=d_tee, b=b_tee, t_f=t_f_tee, t_w=t_w_tee, r=r_1_tee, n_r=n_r)
    .mirror_section(axis='x', mirror_point=[0, 0])
    .align_center()
    .align_to(geometry1, 'top')
)

# total number of individual geometry elements
geo_number = 2

# assemble geometry list
geo_list = [globals()[f'geometry{i}'] for i in range(1, geo_number + 1)]

# create merged section
geometry = sections.CompoundGeometry(geo_list)

# plot geometry
geometry.plot_geometry()

# create mesh
geometry.create_mesh(mesh_sizes=[mesh_area])
# create section
section = Section(geometry=geometry, time_info=True)

# calculate results
section.calculate_geometric_properties()
section.calculate_plastic_properties()
section.calculate_warping_properties()

# plot results
section.plot_mesh()
section.plot_centroids()

# display all results
section.display_results(fmt='.3f')
Agent6-6-6 commented 3 years ago

@connorferster if you comment out the line .mirror_section(axis='x', mirror_point=[0, 0]) in the tee_section code you'll hopefully see the issue of the mesh not being created.

By the way, really liking the .align_to and .align_center options. Makes it really easy to create compound sections vs how it was done previously with the .shift_section being the only option 😄

connorferster commented 3 years ago

Hi @Agent6-6-6,

That is really interesting. Naturally I wanted to check this out right away!

When I tried doing the same, I did not get the erroneous result. Now, I would like to reproduce the result you got so I will do a straight copy-paste of your whole script next.

I tried to upload this as a Gist but Github was giving me an error so apologies for the screenshot: image

connorferster commented 3 years ago

Yeah, copy-pasted your whole script and got the same issue you did. Weird...

connorferster commented 3 years ago

Oh, interesting. The problem seems to occur when the geometry is centered on the origin.

image

In my first script, I missed how you aligned the I-section to the origin, so my script did not work as-is. I had to alter it so that the tee aligned to the center of the I-section and it worked fine.

When I made that one alteration to my script, aligning both the I-section and tee to the origin, then the shear center wigged out. I didn't have a problem with meshing though. Did you have a problem with meshing with your script as you have posted it?

Agent6-6-6 commented 3 years ago

@connorferster , if you take the script I posted and just comment out the mirroring of the tee I see it failing or getting stuck on the mesh creation. Do you get the same behaviour?

connorferster commented 3 years ago

@Agent6-6-6 I do see what you are talking about. The mesh creation seems to "hang".

Interestingly, I don't have the hang problem when the section is not centered on the origin. This centering on the origin seems to be the key to this strange behaviour...

connorferster commented 3 years ago

@Agent6-6-6 I have been playing with this for about 90 mins now and it is so strange. It seems to be something about this particular set of points and facets. Question, if you were to print geometry.points, geometry.facets, geometry.control_points, and geometry.holes and then use those values in v1.0.8, do you get the same hanging behaviour? This is my first time having a "hang" from the meshing. Typically I just get something like a segment fault that crashes my Python kernel.

More weirdness about this:

  1. I put in a bunch of "instrumentation" (i.e. print(...) statements) going from right before meshpy is called in pre.pre.create_mesh() and almost every step back to the original two methods, Geometry.create_mesh() and CompoundGeometry.create_mesh() where I put a print() statement as the first line in both method calls. When I go to call geometry.create_mesh(), I see nothing. No prints, not anything.
  2. If I then change the approach to building the CompoundGeometry as the way I did in my above screenshot then no problems. I see all of the instrumentation print out as expected and it meshes quickly.
  3. This script you have has the same behaviour consistently. It always hangs. However it does not hang if I do use geometry2.align_center(geometry1) instead of using .align_center() on both geometries. Odd...

Given that I cannot see any of my print() statements execute, even from the very beginning of the .create_mesh() method call. I am stumped, I feel like I cannot track down what the problem is if I don't know what code is running and what code is not running! I am at a bit of a loss on this one...Just did a quick search and found some options for debugging this kind of thing on stackoverflow. Will try tomorrow.

In the meantime, I found out why your shear center is coming out funny on the mirrored section. It's the way you are aligning the objects: they are not touching.

When you use align_center() in this manner, where both geometries are aligned to their respective centroids independently and then aligned with align_to(...): image You can see that the resulting SVG is green. This means it's a valid plotly shape because the two shapes are either only touching at a single point or they are not touching.

When you use align_center(geometry1), then you are aligning one geom center to the other geom center and now the SVG is red indicating they are touching at more than one point (i.e., they share a line or are overlapping, which is what we want): image

So, why does this happen? Good question, especially in this case because you are using align_to(geometry1, 'top') and I would have guessed that that would do it. I am guessing it is due to teensy floating point differences. Am interested to see why .align_to(geometry1, 'top') is not fixing it, though.

connorferster commented 3 years ago

@Agent6-6-6

More info:

I am going to focus on fixing the above issue with .align_to() not creating a touching geometry (for this case) and hopefully it will also fix this meshing bug.

Spectre5 commented 3 years ago

Try writing to a file (open in append mode, write, close, and flush it). Or setup python logger to write to file. Maybe print doesn't show up due to buffering?

connorferster commented 3 years ago

@Agent6-6-6 Ah! I think I have got it. It seems that the align_center() and align_to() problems are causing the meshing problem.

In the first instance of geometries not touching and shear center result being incorrect, there is a 0.0000000000001 difference in geometry points where that value represents a gap.

In the second instance with the meshing problems, there is a ~ 0.0000000000001 overlap between the geometries which means that triangle is trying to create a super-small mesh to fill in that overlap sliver.

The reason there is not a problem on my example is because I am not aligning the geometries to the centroid. I have found that when aligning a geometry with the shapely.geometry.Polygon.centroid method and the shapely.affinity.translate() functions (i.e. get the centroid and translate the geometry to the origin) the resulting placement will never place the shape with it's centroid at (0, 0), it will only get to within 14-15 decimal places. A discrepancy small enough to not cause any meaningful errors on the calculation of the plastic centroid but small enough to create a meshing problem.

Now, shapely has a "snapping" function where a geometry can be "snapped" to another and this may serve well. Have played with it a bit but I need to work with it more to see if it is an appropriate fix in this instance.

I am so glad you found this bug!

connorferster commented 3 years ago

@Spectre5 Will try a logging approach. I am still stumped by this "no printing" thing. Will let you know how it goes when I try later this eve.

Spectre5 commented 3 years ago

Maybe there should be a tolerance input and then all points get rounded to that tolerance just before sending to triangle? Surely none of us are working to the 12th digit of precision :)

connorferster commented 3 years ago

...Aaand fixed!

@Spectre5, I implemented your suggestion. The Geometry class now carries a .tol attribute that is set to 12 by default. The value of the attribute represents the number of decimal places of precision to round point coordinates to. The rounding occurs upon instantiation of the Geometry instance during the .compile_geometry() method call. Specifically in the function sections.create_points_and_facets().

@Agent6-6-6 I ran both of your examples that caused problems (shear center and hanging mesh). This change fixed both of them. Your script runs without issue now. Note: that in the shear center example, the SVG still comes out green (which is a bit of a puzzler for me since I confirmed that the points are landing on the exact same y-ordinate) but the meshing and shear centers come out correct. So it seems that shapely recognizes it as a "valid" geometry still and so does section-properties.

It is in the latest pushed commit.

Agent6-6-6 commented 3 years ago

@connorferster, good stuff, easy solution vs trying to debug further, especially if you're coming up against floating point precision issues (which I don't think you'll really solve any other way).

I seem to remember Robbies original clean_geometry module had a similar precision tolerance to merge points and rearrange the mesh if required (discussed and fixed in a prior issue I believe).

I seem to remember now coming across a particular issue previously when using shapely ages ago, when unpacking/unzipping a geometry to x/y coordinates lists in python, some of the values changed slightly from the value in the shapely geometry vs the X and Y unzipped coordinates (probably due to precision issues), I never really looked into it further as I did the same thing rounding to get things to agree so I could compare values after doing some further manipulations.

I guess this highlights the prior discussion we had on having some inbuilt warning regarding when your geometry isn't actually valid, because there is no warning currently unless you are particularly onto it with reviewing the properties and/or plots and realising something went wrong. Even just throwing an exception would stop potentially incorrect results from being used in production. I'm a big fan of protecting the user from themselves in this respect. Hopefully though this change puts all the little niggly misalignments to rest, and any gross misalignments or lack of joining can be observed with plot_geometry!

In the latest push there is still all the additional print statements that I think you added for temporary debugging.

A question, can you actually access the red/green plots you're showing from a script *.py file to check for the validity of the geometry?

Agent6-6-6 commented 3 years ago

@connorferster, I wonder to let shapely deal with it, could the shapely.ops.snap() be used to the same effect with a suitable tolerance. Just curious really if it achieves the same by tidying up geometry at small tolerances?

https://shapely.readthedocs.io/en/stable/manual.html#snapping

connorferster commented 3 years ago

@Agent6-6-6 I am just reviewing the shapely snapping. The function "snaps the vertices in one geometry to the vertices in another". In this example snapping would not help us since we are essentially snapping to a facet. It seems that rounding to a tolerance works for both of these scenarios though.

Note: After implementing the tolerance, I ran the tests and noticed three of the calculations in the test suite were off in the seventh sig fig. I adjusted the test values so tests would pass but made note in the file which values were adjusted and their original value. The original values were from v1.0.8. The affected tests are in test_sections.py for the angle section. Please review and let me know your thoughts on this.

I implemented the tolerance on all Geometry but perhaps I should only implement it for CompoundGeometry?

I have come around now to your way of thinking about notifying the user if there is an issue with their geometry even after we implement techniques for reducing invalids. For disjoint geometry, I think a warning would be appropriate since the disjoint-ism only affects the warping properties calculations. Warnings are big and ugly but don't stop the computation if the user is looking for only geometric quantities.

connorferster commented 3 years ago

And yes, I agree it would be cool to show the status with line colours/weights on the plot_geometry method.

We do have a kind of access to the _reprsvg. It's something I have been thinking of customizing in the future.

Agent6-6-6 commented 3 years ago

@connorferster, I came across one situation today where the creation of a compound geometry wasn't recognising the creation of a small internal hole that was created by moving/aligning together 4 separate geometries creating a small internal rectangle.

Out of town at the moment for a few days, but will post example on return when back at computer. I managed to get it to work eventually by shifting section vs aligning and changing around the order of some of the shift/mirror/align manipulations. So possibly some more precision issues.

Will try boil down to a simple example to demonstrate if I can when I return.

Agent6-6-6 commented 3 years ago

Hi @connorferster

Here's a contrived example showing how two methods to achieve the same thing regarding creating and moving the tee section results in the hole being created either being recognised or not recognised. Appreciate if you could have a look.

Change the which_option variable between 1 or 2 to see the two behaviours.

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

which_option = 2  # 1 or 2

d = 612  # I section depth
b_f = 229  # I-section flange width
t_f = 19.6  # I-section flange thickness
t_w = 11.9  # I-section web thickness
r_1 = 14  # I-section root radii

d_tee = 202  # strengthening tee depth
b_tee = 178  # strengthening tee flange width
t_f_tee = 12.8  # strenghtening tee flange thickness
t_w_tee = 7.8  # strengthening tee web thickness
r_1_tee = 11.4  # strnegthening tee root radii

gap = 2  # gap between section and tee

weld_size = 6  # weld size

n_r = 50  # number of points considered around root radii

mesh_area = 10  # max mesh size

# weld base geometry
weld = sections.Geometry.from_points(points=[[0, 0], [weld_size, 0], [0, weld_size]], facets=[[0, 1], [1, 2], [2, 0]])

# I-Section beam
geometry1 = sections.i_section(d=d, b=b_f, t_f=t_f, t_w=t_w, r=r_1, n_r=n_r).align_center()  # shift_section(-b_f / 2, -d / 2)

# strengthening tee
if which_option == 1:
    # ---- OPTION 1 - HOLE CREATED GETS RECOGNISED
    geometry2 = (
        sections.tee_section(d=d_tee, b=b_tee, t_f=t_f_tee, t_w=t_w_tee, r=r_1_tee, n_r=n_r)
        .mirror_section(axis='x', mirror_point=[0, 0])
        .shift_section(-b_tee / 2, -d / 2 - gap)
    )
elif which_option == 2:
    # ---- OPTION 2 - HOLE CREATED DOES NOT GET RECOGNISED
    geometry2 = (
        sections.tee_section(d=d_tee, b=b_tee, t_f=t_f_tee, t_w=t_w_tee, r=r_1_tee, n_r=n_r)
        .mirror_section(axis='x', mirror_point=[0, 0])
        .align_center()
        .align_to(geometry1, 'top')
        .shift_section(0, -gap)
    )

# weld 1 & 2
geometry3 = weld.mirror_section(axis='x', mirror_point=[0, 0])
geometry3 = geometry3.shift_section(t_w_tee / 2, -d / 2)
geometry4 = geometry3.mirror_section(axis='y', mirror_point=[0, 0])

# total number of individual geometry elements
geo_number = 4

# assemble geometry list
geo_list = [globals()[f'geometry{i}'] for i in range(1, geo_number + 1)]

# create merged section
geometry = sections.CompoundGeometry(geo_list)

geometry.plot_geometry()

# CREATE MESH
geometry.create_mesh(mesh_sizes=[mesh_area])

# create section
section = Section(geometry=geometry, time_info=True)

# calculate results
section.calculate_geometric_properties()
section.calculate_plastic_properties()
section.calculate_warping_properties()

# plot results
section.plot_mesh()
section.plot_centroids()

# display all results
# check https://sectionproperties.readthedocs.io/en/latest/rst/post.html for definitions
section.display_results(fmt='.3f')
Agent6-6-6 commented 3 years ago

Note there is no longer an add_hole procedure, so even if you recognise the issue you cannot manually correct it. But unsure why it is actually occurring, especially after you added in the tolerance rounding (unsure if that's at play here).

connorferster commented 3 years ago

Hi @Agent6-6-6, I am so glad you are finding these bugs. I hope to be able to fix this one but, if not, it can at least be addressed in the documentation.

The problem in this instance seems to be the geometry1.align_center(). Aligning compound geometries to the origin seems to create floating point errors in ways that don't seem to crop up otherwise because each shape is aligning to two "different" origins. For example, if you replace the .align_center() on geometry1 and restore the .shift_section(...) that you have commented beside it, then the problem goes away, too.

The reason the .tol feature does not fix this issue is because the .tol only applies to geometry information that is passed into the Section. By introducing .tol in the way that I have, there is now a disconnect between the shapely geometry and the geometry data (i.e. .points, .facets, etc) that is used for calculating the section properties.

It would be good if that disconnect did not exist and for the .tol to apply to both the shapely geometry at instantiation and the analysis geometry. I am going to play around with this and see what I can come up with.

connorferster commented 3 years ago

It's also worth mentioning that, for this problem, an .add_hole() method would not fix it simply because the gap is not a fully enclosed space. The way that holes are recognized in CompoundGeometry is by performing a union with all of the constituent shapes in the CompoundGeometry. Once the union is complete, I simply query how many interior regions are now found in the unified shape. Because I trust shapely, I think that if there has been an interior region created, it will always be recognized. In this case though, no interior region has been created due to these floating point errors.

Agent6-6-6 commented 3 years ago

@connorferster, I see putting in a few print statements in align_center() that you indeed get some minor little bits added on.

I can solve the issue by rounding the centroid variables cx, cy to a tolerance in align_center() method. Unsure if that affects anything else though with other examples when centroid isn't at zero/zero point. The method is also called by the calculation of plastic properties for example and have not looked at what it's doing in there.

connorferster commented 3 years ago

@Agent6-6-6 Great idea! I have implemented it and it fixes the problem in the script you provided and it passes all of the tests.

Agent6-6-6 commented 3 years ago

Hi @connorferster

Come across another issue, it's to do with the creation of compoundgeometry from overlapping geometry and the meshing not working as intended. Sort of related to #10 in a way where I first mentioned possibly using shapely to overcome the meshing issue. https://github.com/robbievanleeuwen/section-properties/issues/10#issuecomment-508024727

As an example, in the following you'll note that the mesh is only applied correctly to the part of the individual regions where the control point is located. Unlike the solution to #10, you get an exception when you try to add two control points per region?

import sectionproperties.pre.sections as sections
from sectionproperties.pre.pre import Material
from sectionproperties.analysis.cross_section import Section

geometry_1 = sections.i_section(d=203, b=133, t_f=7.8, t_w=5.8, r=8.9, n_r=8)
geometry_2 = geometry_1
compound = geometry_2.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=90, rot_point=[0, 0]) + geometry_1
compound.plot_geometry()
mesh_area = 3
compound.create_mesh(mesh_sizes=[mesh_area])
section = Section(geometry=compound, time_info=True)
section.plot_mesh()

Figure_1

If we were not limited to one control point we could either fix this manually by adding further control points. (is there a way of appending further single extra control points like previous rather than requiring to replace all of them as .control_points now seems to do?).

Alternatively and ideally if you split/union the entire compound geometry into all the individual polygons making up the compound section (which I believe you've already done elsewhere for other operations anyway), you can then add a single control point in each of the resulting constituent polygons that make up the entire section, and in the process solve the meshing issue once and for all seamlessly without any user intervention adding extra control points?

Hopefully that made sense!

Agent6-6-6 commented 3 years ago

@connorferster, one more small issue I just noted. If you manually apply control_points to a geometry, and then do some manipulation like rotating, the defined custom control points are not also rotated. They are instead replaced with a representative_point from shapely operation (EDIT - as presumably the control points defined would now lie outside of the rotated cross section).

For example:-

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

geometry_1 = sections.i_section(d=203, b=133, t_f=7.8, t_w=5.8, r=8.9, n_r=8)
geometry_2 = geometry_1
compound = geometry_2.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=90, rot_point=[0, 0]) + geometry_1
compound.control_points = [[5, 25], [25, 5]]
compound.plot_geometry() #note custom control points
compound = compound.rotate_section(angle=90, rot_point=[0, 0])
compound.plot_geometry() #note custom control points are gone and control point via representative_point is provided

The expected behaviour to me would be that the defined control points are also rotated with the compound geometry and retained?

Agent6-6-6 commented 3 years ago

@connorferster, thinking out loud on the meshing and automatically adding control points for overlapping geometry.....

This is basically what I meant regarding dividing geometry into all the "bits" primarily to determine a control point for each bit to be applied to the original compound geometry for creating the mesh. Obviously simple with 2 geometries..... may be harder with more but presumably just a matter of doing some list comprehension with the constituent geometries?

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

geometry_1 = sections.i_section(d=203, b=133, t_f=7.8, t_w=5.8, r=8.9, n_r=8)
geometry_2 = geometry_1.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=90, rot_point=[0, 0])
compound = (geometry_2 ^ geometry_1) + (geometry_2 & geometry_1) # need to do the equivalent of this operation for all geometries to determine all 'bits' 
compound.plot_geometry()  # note custom control points in all bits for meshing to work as intended

# rest is just showing mesh now working correctly
mesh_area = 3
compound.create_mesh(mesh_sizes=[mesh_area]) 
section = Section(geometry=compound, time_info=True)
section.plot_mesh()

The only tricky bit is determining in which original section the point is to determine which mesh should apply (and in bits that existed in both maybe taking the more refined mesh value?). This is probably a matter of running through all the new control points and testing which original geometry contains that point using shapely and assembling a new mesh size list for each of the bits created in the process. I'm guessing you'd just do this in the mesh creation process, so these control points are just an arbitrary creation at that point in time for the purposes of meshing.

EDIT - gets infinitely more complicated if you have overlapping materials as you'd have to have some way of saying which material takes precedence in any overlaps?

EDIT2 - I think as well something like this if it could be implemented would solve your control point issue with the composite concrete column with steel in the example gist you provided at the beginning?

Agent6-6-6 commented 3 years ago

If we were not limited to one control point we could either fix this manually by adding further control points. (is there a way of appending further single extra control points like previous rather than requiring to replace all of them as .control_points now seems to do?).

@connorferster, I figured out I can just do this:- compound.control_points.append([5, 5]) but I wonder if there should be some built in simple method to achieve the same without needing to know the variable name for the control points to add to it, like an add_control_point method simply to append a single or series of control points to an existing number of control points?

Edit - could have optional variable within this type of method to replace all control points if that was required (vs appending)?

Agent6-6-6 commented 3 years ago

@connorferster, a few more small niggles I found while just messing around trying to see if I could break things :-

item 1

This example results in the code displaying the plot geometry, then it exits with no exception. Suspect it's another precision issue with rotating about [0, 0] point, if you change to [0.00000001, 0] for example it will complete without exiting with no warning?

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

geometry_1 = sections.i_section(d=203, b=133, t_f=7.8, t_w=5.8, r=8.9, n_r=8)
geometry_2 = geometry_1.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=90, rot_point=[0, 0])
geometry_3 = geometry_1.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=45, rot_point=[0, 0])
compound = geometry_1 + geometry_2 + geometry_3
compound.plot_geometry()
mesh_area = 3
compound.create_mesh(mesh_sizes=[mesh_area])
section = Section(geometry=compound, time_info=True)
section.plot_mesh()

item 2

This example results in an unhandled exception in plot_geometry (note have just changed the alignment to 'bottom' for geometry_3, otherwise the same code as item 1 above. Not quite sure what is occurring here but may also be precision related?

import sectionproperties.pre.sections as sections
from sectionproperties.analysis.cross_section import Section

geometry_1 = sections.i_section(d=203, b=133, t_f=7.8, t_w=5.8, r=8.9, n_r=8)
geometry_2 = geometry_1.align_center(geometry_1).align_to(geometry_1, 'top').rotate_section(angle=90, rot_point=[0, 0])
geometry_3 = geometry_1.align_center(geometry_1).align_to(geometry_1, 'bottom').rotate_section(angle=45, rot_point=[0, 0])
compound = geometry_1 + geometry_2 + geometry_3
compound.plot_geometry()
mesh_area = 3
compound.create_mesh(mesh_sizes=[mesh_area])
section = Section(geometry=compound, time_info=True)
section.plot_mesh()

Error:-

Traceback (most recent call last):
  File "....................shapely-section-properties/example.py", line 13, in <module>
    compound.plot_geometry() 
  File "....................shapely-section-properties\sectionproperties\pre\sections.py", line 621, in plot_geometry
    ax.plot(h[0], h[1], "rx", markersize=5, label="Holes")
IndexError: tuple index out of range
connorferster commented 3 years ago

Interesting...looking forward to checking these out!

connorferster commented 3 years ago

@Agent6-6-6 I took a look a these. For Item 1, I was not able to reproduce. I was able to copy-paste the example you gave and it would run without interruption. If you try it again, do you get the same behaviour? I would like to recreate this and fix anything that needs fixing because the crashing-without-raising-an-exception-thing is not good.

Item 2: this was a coding error on my part. I thought I was getting a tuple from shapely but it was giving me a nested tuple. Easy fix.

While I was in there, though, I realized that I had some errors in Geometry.align_to() and the doc-string was incomplete. Also decided it would be very useful to be able to change the size/resolution of the resulting figure from Geometry.plot_geometry() so I implemented that, also.

Next, implement these persistent control_points and continue updating the Sphinx docs.

Agent6-6-6 commented 3 years ago

@connorferster, for item 1, this now works for me with your latest commits. Guessing your fixes to align_to module fixed something along the way that was causing the issue 👍.