calculations with sun and shadows

A forum dedicated to the Draft, Arch and BIM workbenches development.
Forum rules
Be nice to others! Respect the FreeCAD code of conduct!
Post Reply
lambda
Posts: 91
Joined: Sat Feb 25, 2017 3:10 pm
Contact:

calculations with sun and shadows

Post by lambda »

I wrote a few simple functions to calculate whether an objects shadow touches a given point at some times of the day. (Or generally whether some fixed point in space (like the sun) is or isn't visible. You want to listen in on NASA's deep space probes, you can calculate your time windows here.)

Maybe somebody else finds this helpful or even gets inspired to write better tools.

You use it like this:

Code: Select all

doc = App.ActiveDocument
axis = north3D(doc.Site)
sun_summer = sun_direction(doc.Site, 6, 21).multiply(doc.Box.Shape.BoundBox.DiagonalLength)
vCone = visibilityCone(FreeCAD.Vector(0,0,0), sun_summer, axis)
Part.show(vCone, "SunSummer")  # For debugging or visualisation only
getShadowTimes(vCone, doc.Box.Shape)
The last line returns a list of tuples. Each tuple has the start/end time in hours when there is shadow. In my example I got

Code: Select all

[(4.065269158869852, 4.995921816460562), (19.00407818353943, 19.934730841130147)]
Here is the definition of the functions:

Code: Select all

def north3D(site):
        "Direction of the earth axis in the local coordinate System of Arch Site"
        from math import cos, sin, pi
        from FreeCAD import Vector
        lat = site.Latitude * pi / 180
        decl = site.Declination.Value * pi / 180
        return Vector(cos(lat)*sin(-decl), cos(lat)*cos(-decl), sin(lat))

def sun_direction(site, month, day):
        from pysolar.solar import datetime, get_altitude_fast
        from math import cos, sin, pi
        from FreeCAD import Vector
        dt = datetime.datetime(2000, month, day, 0, tzinfo=datetime.timezone.utc)
        latitude = App.ActiveDocument.Site.Latitude
        alt = get_altitude_fast(latitude, 0, dt) * pi / 180
        decl = App.ActiveDocument.Site.Declination.Value * pi / 180
        return Vector(cos(alt)*sin(-decl), cos(alt)*cos(-decl), sin(alt))

def visibilityCone(pnt, dir, axis, cutBelowHorizon=True):
        "Sweep direction along the path it takes by earth rotation"
        import Part
        from FreeCAD import Vector
        dirline = Part.makeLine(pnt, pnt + dir)
        cone = dirline.revolve(pnt, axis, 360)
        if not cutBelowHorizon:
                return cone
        air_len = 4 * dir.Length
        air = Part.makeBox(air_len, air_len, air_len, pnt + air_len/2 * Vector(-1, -1, 0))
        return cone.common(air).Faces[0]

def add_times(ts, start, end):
        if not ts:
                ts.insert(0, (start, end))
                return ts
        for i, (s, e) in enumerate(ts):
                if start <= s:
                        break
                if end <= e:  # Case within
                        return ts
                if start <= e:
                        break
        if end < s:  # Case total left
                ts.insert(i, (start, end))
                return ts
        if start > e:  # Case total right
                ts.insert(i + 1, (start, end))
                return ts
        if start <= s:
                ts[i] = (start, e)
        if end >= e:
                ts[i] = (ts[i][0], end)
        while i < len(ts) - 1 and ts[i+1][0] <= ts[i][1]:
                ts[i] = (ts[i][0], max(ts[i+1][1], ts[i][1]))
                del ts[i+1]
        return ts

def getShadowTimes(vCone, shape):
        from math import pi
        xs = vCone.common(shape)
        times = []
        for f in xs.Faces:
                (umin, umax, vmin, vmax) = f.ParameterRange
                shadow_start = umin * 12 / pi
                shadow_end = umax * 12 / pi
                add_times(times, shadow_start, shadow_end)
        return times
Edit: changed the title to better reflect where this ended up going
Last edited by lambda on Fri Aug 26, 2022 2:47 pm, edited 1 time in total.
paullee
Veteran
Posts: 5097
Joined: Wed May 04, 2016 3:58 pm

Re: shadows for calculations

Post by paullee »

Sound interesting :)

Not having pysolar, can't try; can you post some screencapture to illustrate how does it works ?

[EDIT] - First thing noted is it needs the Arch Site object, that Latitude and Longitude.
lambda
Posts: 91
Joined: Sat Feb 25, 2017 3:10 pm
Contact:

Re: shadows for calculations

Post by lambda »

Hm, pysolar is part of most distribution. Would a dependency on ladybug be better? The calculation of the sun's position is involved enough, that I don't want to do that myself. Though for energy planning applications you don't need quite as precise values as pysolar calculates.

There isn't much to show on screen. There are no GUI tools, right now this is just calculations, that only are usable from the python console. But if somebody uses this to build GUI tools, that would be awesome. I myself probably will work on more calculations, but not on tools.
User avatar
Kunda1
Veteran
Posts: 13434
Joined: Thu Jan 05, 2017 9:03 pm

Re: shadows for calculations

Post by Kunda1 »

Didn't yorik deprecate use of pysolar and used ladybug instead for Arch ?
Alone you go faster. Together we go farther
Please mark thread [Solved]
Want to contribute back to FC? Checkout:
'good first issues' | Open TODOs and FIXMEs | How to Help FreeCAD | How to report Bugs
lambda
Posts: 91
Joined: Sat Feb 25, 2017 3:10 pm
Contact:

calculations with sun and shadows

Post by lambda »

I have extend the code a bit. There still isn't any visualization, but at least you can interact with it from TreeView / PropertyEditor now. Also I have an example file, where you can test the code on:

Open the file and you can try this example session:

Code: Select all

>>> import sunandshadows as sns
>>> # Gui.Selection.clearSelection()
>>> # Gui.Selection.addSelection('PV_Sulz','Roof','Face3',4062.01,3713.72,7404.89)
>>> obj = sns.makeSNSResults('PV')
>>> summer_chimney = obj.Proxy.calculate(obj, 6, 21, [App.ActiveDocument.Structure])
>>> sns.summaryV(summer_chimney.shadows)
array([['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.24', '0.74', 'None', 'None', '3.17', '3.55'],
       ['0.00', '0.00', '0.00', '0.16', '0.41', '0.73', '1.02', '1.25', '1.61', 'None', 'None', '5.27', 'None'],
       ['0.27', '0.47', '0.60', '0.68', '0.77', '0.89', '0.94', '0.87', '0.77', '0.60', '0.30', '0.00', '0.00'],
       ['0.52', '0.57', '0.60', '0.52', '0.43', '0.31', '0.16', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.31', '0.23', '0.13', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00'],
       ['0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00', '0.00']], dtype='<U4')
You can also inspect 'summer_chimney.shadows' for the exact times of shadows. Or you can add the neighboring houses to the list of obstacles and get a combined analyses.

I might work on radiation intensity next. Then I could combine radiation intensity with shadowing and calculate the total received energy over a day or even year for any given point.
Attachments
sunandshadows.py
(7.19 KiB) Downloaded 23 times
PV-Sulz.FCStd
(70.71 KiB) Downloaded 22 times
User avatar
ragohix769
Posts: 565
Joined: Sat Jul 18, 2020 7:04 am
Location: Rome - Italy

Re: calculations with sun and shadows

Post by ragohix769 »

lambda wrote: Fri Aug 26, 2022 2:45 pm
I might work on radiation intensity next. Then I could combine radiation intensity with shadowing and calculate the total received energy over a day or even year for any given point.
Absolutely INTERESTING! I opened the fcad file, but I don't understand how to use the script. I have to install pysolar? But Yorik not said that it's depecrecated?
After #ElonMuskBuyTwitter I'm no more on Twitter, that's really enough :-(
=> Now you can find me here on #Mastodon: https://mastodon.uno/@opensoul - I hope more people do the same :-)
lambda
Posts: 91
Joined: Sat Feb 25, 2017 3:10 pm
Contact:

Re: calculations with sun and shadows

Post by lambda »

Most functions in the module work completely independent from pysolar. But yes: To reproduce the example session above, you would need to have pysolar installed. However, I only use it to get the sun's postion. It will be easy to port to ladybug. I only used pysolar, because it is part of Debian and ladybug isn't.

Is there anything else you need, to try the code?
User avatar
ragohix769
Posts: 565
Joined: Sat Jul 18, 2020 7:04 am
Location: Rome - Italy

Re: calculations with sun and shadows

Post by ragohix769 »

lambda wrote: Sat Aug 27, 2022 11:46 am
Is there anything else you need, to try the code?
I'll try it
After #ElonMuskBuyTwitter I'm no more on Twitter, that's really enough :-(
=> Now you can find me here on #Mastodon: https://mastodon.uno/@opensoul - I hope more people do the same :-)
Post Reply