Set Up the Grids
Create a rectangular grid in Revit using this sketch as a guide. Create a Python script for Dynamo 3.x if needed, and deliver the script as a downloadable Python file
I'll check what Revit tools are available on your computer, then build the grid (5 vertical lines at 15' spacing, 6 horizontal lines spaced 30/10/10/20/20 from the top).
I couldn't create the grid in Revit for you. The Revit connection can see your open session but has no tool for adding elements, so I wrote a Dynamo 3.x script instead (create_grid_from_sketch.py). I checked that it has no syntax errors, but I haven't run it in Revit.
Here's how I read your sketch:
- Vertical grids 1–5: 4 bays at 15' each, 60' across in total. That matches the 60' wall already in your model.
- Horizontal grids A–F, top to bottom: A to B is 30', B to C is 10', C to D is 10', D to E is 20', E to F is 20'. That's 90' in total.
- Placement: grids 1 and F cross at (0, 0), and every line runs 5' past the outer grids.
To run it: in Dynamo, add a Python Script node, set it to CPython3 or PythonNet3, paste in the file and click Run. You can add three optional inputs to the node: the X and Y position of the grid 1 / F corner, and how far the lines run past the outer grids. If a grid with one of these names already exists, the script skips it rather than creating a duplicate.
Before you run it: the script puts the grid 1 / F corner at the project origin. To line the grid up with your wall, give me the wall's endpoint coordinates (or tell me which grid line it sits on) and I'll set the defaults to match.
I've updated create_grid_from_sketch.py so Grid A to Grid B is now 20' instead of 30'. The grids now run 80' from top to bottom: 20', 10', 10', 20', 20'. Grid A moves down to y = 80'. Grids B through F and the vertical grids 1–5 stay where they were.
If you already ran the first version, delete grids A–F in Revit before running this one. Otherwise the script skips grids whose names already exist, so it won't move A.
Move the existing grids as needed to provide space for a 40' x 40' atrium at the center of the building.
Delete Grids B, E, and 4
The Revit connection still can't change the model, so here's a script that deletes those three grids by name.
I couldn't delete grids B, E and 4 directly because the Revit connection still can't change the model. Instead, delete_grids_by_name.py deletes them when you run it in a Dynamo Python Script node (CPython3 or PythonNet3). It has no syntax errors, but I haven't run it in Revit. Ctrl+Z in Revit undoes the whole run.
If you've already run move_grids_for_atrium.py, the remaining grids will be evenly spaced in both directions, with bays of 20' / 40' / 20':
- Vertical: 1 at 0', 2 at 20', 3 at 60', 5 at 80'
- Horizontal: F at 0', D at 20', C at 60', A at 80'
The atrium stays between grids 2–3 and D–C.
Grid 5 now sits right after grid 3 and there's no grid 4, so you may want to rename 5 to 4. I can add that to the script.
Add Structural Framing
Add W10x49 columns between Level 1 and Level 2 at each grid intersection with W12x26 beams connecting the tops of the columns
The Revit connection still can't place elements, so I'll write this as a Dynamo script too. The script finds every grid intersection in the model, so it works with whatever grids you have now.
I couldn't place the steel myself, so I wrote a Dynamo script for it: place_steel_frame.py. It has no syntax errors, but I haven't run it in Revit. Ctrl+Z in Revit undoes the whole run.
When you run it, it places:
- Columns: a W10x49 at every grid intersection, from Level 1 up to Level 2.
- Beams: a W12x26 along each grid line between neighboring columns, with the top of steel at Level 2.
The script works from whatever grids are in the model when you run it. With grids 1, 2, 3, 5 and A, C, D, F, that's 16 columns and 24 beams. Beams run along the atrium edges on grids 2, 3, C and D, and nothing crosses the 40' × 40' opening.
- Family types: if W10X49 and W12X26 aren't loaded yet, the script tries to load just those two types from the standard Revit library. If the library isn't installed, load W Shapes-Column and W Shapes through Insert › Load Family and run it again.
- Levels: the script needs levels named "Level 1" and "Level 2", with Level 2 above Level 1. If one is missing, it stops and lists the levels it found.
- Running it twice: it skips places that already have a column or beam, so it won't create duplicates.
The beams around the atrium edge span 40'. A W12x26 is likely undersized for that span, so it's worth having those beams checked.
The Revit connection still can't change the model, so here's a script that adds the two levels.
I couldn't add the levels directly because the Revit connection still can't change the model, so I wrote a Dynamo script for it: create_levels_above.py. It has no syntax errors, but I haven't run it in Revit.
- What it creates: Level 3, 10'-0" above Level 2, and Level 4, 10'-0" above Level 3. It also makes a floor plan view for each one, the same as drawing a level by hand does.
- Re-running it: if a level with that name or at that height already exists, it skips that level, so it won't make duplicates.
- How to run it: paste it into a Python Script node (CPython3 or PythonNet3) and click Run. Ctrl+Z in Revit undoes the whole run.
A 10' floor-to-floor height leaves only about 9' under the W12 beams, before ductwork and ceilings. That's tight if these will be occupied floors.
If you want the steel frame on the new floors too, I can extend place_steel_frame.py to place columns from Level 2 to 3 and 3 to 4, with beams at each level.
Copy the beams and columns supporting Level 2 to Levels 3 and 4
The Revit connection still can't change the model, so I'll write this one as a Dynamo script too.
I wrote copy_frame_to_upper_levels.py, a Dynamo script that copies the Level 2 steel up to Levels 3 and 4. The Revit connection still can't change the model, so I couldn't make the copies myself. The script has no syntax errors, but I haven't run it in Revit.
What it copies:
- the columns that run from Level 1 to Level 2
- the beams referenced to Level 2
Where the copies go:
- columns from Level 2 to Level 3, with beams at Level 3
- columns from Level 3 to Level 4, with beams at Level 4
With the current grid, that's 32 new columns and 48 new beams.
- Levels: each copy is tied to its own levels with zero offsets. So if you move Level 3 or 4 later, the columns and beams move with it. At the end, the script checks that every new column is on the right levels and lists any that aren't.
- Settings: because the elements are copied rather than redrawn, the W10x49 and W12x26 types, the rotation and the other instance settings carry over.
- Order: run
create_levels_above.pyfirst. If Level 3 or 4 is missing, this script stops and tells you. - Re-running: it skips any spot that already has a matching column or beam, so running it twice won't create duplicates. Ctrl+Z undoes the whole run.
The new columns are separate one-story pieces stacked at each level. The same W10x49 size runs all the way up, so it's worth having the engineer check the ground-floor columns, which now carry three levels.
Cantilever the beams at Level 4 on the south side of the building 10' to the exterior of Grid F
I'll treat Grid F as the south edge, since it's at the bottom of your plan. The script will lengthen the north–south beams that currently stop at Grid F so they run 10' past it. Each one becomes a single continuous beam: a 20' back-span from D to F, then a 10' cantilever. That's a true cantilever, not a separate stub bolted to the column.
I wrote cantilever_level4_south.py to make the cantilevers. The Revit connection still can't change the model, so you'll need to run it in Dynamo. It has no syntax errors, but I haven't run it in Revit.
What it does: it extends the four Level 4 beams that run north–south on grids 1, 2, 3 and 5. Each beam now stops 10' past Grid F, at y = −10', so it's 30' long: the 20' span from D to F plus a 10' cantilever.
- What stays the same: the beams along Grid F and everything on Levels 2 and 3 are left alone.
- Direction: "exterior" means the side of Grid F away from the center of the building, so it works wherever the grid sits in the project. I assumed Grid F is the south side because it's at the bottom of your sketch.
- Running it: paste it into a Python Script node (CPython3 or PythonNet3) and run. Running it twice won't extend the beams again, and Ctrl+Z undoes the whole run.
Two things to sort out after you run it:
- Column connections: for the beams to work as cantilevers, they need to be continuous or moment-connected over the Grid F columns. That should go to the engineer, along with the W12x26 size.
- Free tips: the cantilever ends aren't tied together. I can add a W12x26 edge beam along y = −10' to connect them.
Add W12x26 spandrel beams at the end of the cantilevered beams
The Revit connection still can't change the model, so here's another Dynamo script. It reads where the cantilever tips are and connects them.
I wrote add_spandrel_level4_south.py. The Revit connection still can't change the model, so I couldn't add the beams myself. The script has no syntax errors, but I haven't run it in Revit.
It places three W12x26 spandrel beams at Level 4, along y = −10', connecting the tips of the cantilevers:
- Grids 1 to 2: 20'
- Grids 2 to 3: 40', across the atrium bay
- Grids 3 to 5: 20'
- How it finds the tips: it looks for Level 4 beam ends that sit 10' past Grid F, rather than using fixed coordinates. That means you have to run
cantilever_level4_south.pyfirst; if the tips aren't there, the script stops and tells you. - Running it twice: it skips any span that already has a beam, so you won't get duplicates. Ctrl+Z undoes the whole run.
The 40' spandrel hangs off the ends of two cantilevers with nothing under it, so W12x26 is especially likely to be too light there. That one is worth checking with the engineer along with the cantilevers.
Add Facade Elements
Create curtain walls on the exterior faces of the building using a 5' vertical grid spacing
The Revit connection still can't change the model, so this will be a Dynamo script as well. Here are the defaults I'm using; each is a setting at the top of the script:
- Location: the perimeter is the outer grid lines (1, 5, A, F). The walls sit 1'-0" outside the grid lines so they clear the W10 column faces.
- Height: Level 1 to Level 4, all four sides.
- South side: the wall stays at Grid F, under the cantilevered overhang.
- Grid: vertical gridlines at 5'-0", centered on each wall. No horizontal grids, since you only mentioned vertical.
I wrote create_perimeter_curtain_walls.py, which creates four curtain walls around the building, one per side. The Revit connection still can't change the model, so you'll need to run it in Dynamo. It has no syntax errors, but I haven't run it in Revit.
- Footprint: each wall is 82' long and sits 1'-0" outside the outer grid lines (1, 5, A, F). That's x = −1' to 81' and y = −1' to 81'.
- Height: each wall runs from Level 1 to Level 4.
- Wall type: it creates a new curtain wall type, "Curtain Wall - 5ft Vertical", copied from your basic curtain wall. That type has vertical gridlines at a fixed 5'-0" and no horizontal gridlines.
- Orientation: the walls are drawn clockwise, so their exterior faces point outward.
- Running it: paste it into a Python Script node (CPython3 or PythonNet3) and run. Running it twice won't create duplicate walls, and Ctrl+Z undoes the whole run.
Things to check afterwards:
- End panels: an 82' wall doesn't divide evenly by 5'. With the gridlines centered, each wall gets 16 full panels plus a 1' panel at each corner. Setting the offset to 2'-6" instead gives 85' walls with exactly 17 panels, but the glass would sit farther from the columns.
- Gridline centering: I wasn't sure the parameter that centers the gridlines can be set this way. If it can't, the panels will start at the corner where you began drawing and leave a 2' piece at the other end. You can change this in the walls' Justification setting.
- South side: the Level 4 cantilever beams cross the glass line at the top of the south wall. That edge will need a detail where the steel passes through.
- Original wall: the generic 8" wall from the start is still in the model.
Note: Claude remembered that I had offset the curtain walls 1’ outside the column grids during a previous conversation. So, it automatically applied the same rule here.
Change the curtain wall pattern to use Vertical Grids spaced at 2.5' apart. Justify the pattern to the Center of each curtain wall and alternate the curtain panels between glazed and solid panels in a checkboard pattern.
A checkerboard needs rows as well as columns. Right now the walls only have vertical gridlines, so each panel is a full-height strip and alternating them would just give stripes. I need horizontal gridlines as well.
Got it: one row per floor. I'll put the horizontal gridlines exactly at the Level 2 and Level 3 heights instead of a fixed 10', so the rows line up with the floors even if a level moves.
I wrote curtain_wall_checkerboard.py to update the four perimeter curtain walls. The Revit connection still can't change the model, so you'll need to run it in Dynamo. It has no syntax errors, but I haven't run it in Revit.
- Vertical grid: gridlines every 2'-6", centered on each wall. On the 82' walls that gives 32 full panels with a 1' panel at each corner.
- Rows: horizontal gridlines sit exactly at the Level 2 and Level 3 heights, so each floor is one row of panels.
- Checkerboard: panels alternate Glazed and Solid by column and by floor. The bottom-left panel of each wall starts solid. The 1' corner panels are part of the pattern.
- Wall type: the existing type is updated in place and renamed "Curtain Wall - 2.5ft Vertical".
- Running it: paste it into a Python Script node (CPython3 or PythonNet3) and run. Running it again won't add duplicate gridlines or panels, and Ctrl+Z undoes the whole run.
Before you run it:
- Panel types: the script needs the standard "Glazed" and "Solid" System Panel types. If either is missing, it stops and lists the panel types it found.
- Pinned panels: it unpins the panels so it can change their type. If you later change the panel setting on the wall type, these panels won't follow that change.
- Corners: the pattern starts at the start of each wall, so where two walls meet, the panels on either side may be the same type. I can make the pattern carry around the corners if you'd like.
Add Floors and a Roof
- Add a concrete floor slab on Level 1
- Add a floor type with bubble slabs on Levels 2 and 3.
- Add a roof at Level 4.
- All floors and roofs should honor the 40' x 40' atrium at the center of the building.
Here's how I'll handle the parts you didn't specify. Each one is a setting at the top of the script:
- Level 1 atrium: I'll leave the Level 1 slab solid, so the atrium has a floor. "Honoring the atrium" there would otherwise mean a hole in the ground floor. It's a one-line switch if you do want the opening.
- Edges: slab and roof edges follow the curtain wall line, 1'-0" outside the outer grids. The Level 4 roof also reaches out over the south cantilever to the spandrel line, at y = −10'.
- Atrium: the script finds the atrium from the grid itself, as the center bay between grids 2–3 and D–C. The opening edges sit on those grid lines.
I wrote create_floors_and_roof.py, which adds the slabs and the roof. The Revit connection still can't change the model, so you'll need to run it in Dynamo. It has no syntax errors, but I haven't run it in Revit. Running it twice won't create duplicates, and Ctrl+Z undoes the whole run.
Level | Element | Type | Atrium |
1 | Floor, 82' × 82' | New "Concrete Slab - 6in" | Solid (switch available) |
2 | Floor, 82' × 82' | New "Bubble Deck Slab - 11in" | 40' × 40' opening |
3 | Floor, 82' × 82' | "Bubble Deck Slab - 11in" | 40' × 40' opening |
4 | Flat roof, 82' × 91' | Your existing generic roof type | 40' × 40' opening |
- Bubble deck type: the slab is built in three layers: 3" concrete on top, a 5" core with a new "Concrete - Voided (Bubble Deck)" material, and 3" concrete on the bottom. The plastic voids themselves aren't modeled; the core material just marks where they are for schedules and takeoffs.
- Roof: it runs out over the south cantilever to the spandrel line, which is why it's 91' deep. The edges are set so the roof stays flat.
To check after running:
- Slabs and steel overlap: the beams are set with their tops at each level, and the slabs are also set with their tops at each level. The steel therefore sits inside the slab thickness. Lowering the beams by the slab depth (11" at Levels 2–3) would put them under the slab. I can script that.
- Roof atrium opening: this leaves the atrium open to the sky. If you'd rather close it with glass, I can add a sloped glazing or skylight roof over the opening.
Dynamo returned this error: PythonEvaluator.Evaluate operation failed. Input compound structure has wrong EndCap condition for this element type. Parameter name: srcStructure
The error comes from the wall-style end-cap setting on the new layer structure, which floors don't accept. I'll change the script to edit the floor type's own existing layer structure instead of building a new one.
I've fixed create_floors_and_roof.py, but I haven't run the new version in Revit. The script still passes a syntax check.
What went wrong: the script built the new floor's layers from scratch. A layer set made that way has a wall-style end-cap setting, and Revit refuses that on floors. That caused the "wrong EndCap condition" error.
The fix: the script now copies an existing floor type, which already has floor-valid settings, and only swaps in the new layers.
Re-running it: the failed run may have left behind a "Concrete Slab - 6in" type with the generic floor's layers. That's fine. The script now resets the layers every time it runs, so the next run will correct that type. Nothing else was created before the error, so you can re-run without cleaning anything up first.
Create Offices
- Create offices around the perimeter of Levels 2 and 3 using this sketch as a guide.
- Adjust the office depth as needed to provide a 4' wide corridor of free space around the atrium.
- And place the furniture in each office.
I'll lay this out as a ring of offices around the atrium. I'll show you the plan first, then write the Dynamo script.
Now I'll check the geometry outside Revit and draw the plan, so I can confirm the corridor clearance and the door frontages before you run it.
The geometry works: 4'-6" of clear corridor to the atrium edge, 4'-1" past the corner columns, and every office, including the corners, has a door onto the corridor.
I wrote create_perimeter_offices.py, which builds the offices on Levels 2 and 3. I checked the layout math outside Revit and drew the plan you see (layout.png), but I haven't run the script in Revit itself.
Layout
- Office count: 16 per floor, 32 in total. Each side has 3 standard offices about 14'-11" wide, close to your 15', plus a 21' corner office.
- Office depth: reduced from your sketch's 20' to about 16'-4". That leaves a 4'-6" corridor from the office wall to the atrium edge, and 4'-1" past the columns at the atrium corners.
- Corner offices: with a simple ring, the corner rooms would have no wall touching the corridor. So the offices are arranged in a pinwheel, and each corner office gets about 4'-4" of corridor wall for its door.
- Columns: the corner-office walls line up with grids 2/3/C/D, so they run through those columns instead of sitting next to them.
What goes in each office (following your sketch)
- Door: at the corridor-side corner, swinging into the office.
- Bookshelves: two units against the corridor wall.
- Desk area: the desk sits mid-room, with the desk chair on the window side and two guest chairs facing it from the corridor side.
- Corner offices: the same arrangement, mirrored so the door lands on the corridor side.
- Rooms: named "Office" and numbered 201–216 and 301–316.
Walls: the partitions use your first "Interior" wall type and stop 11" below the slab above.
Before you run it
- Furniture families: if no desk, chair or shelving families are loaded, the script loads the first match from the Revit library. Load your own beforehand if you have preferred ones, and it will use those instead.
- If furniture faces the wrong way: families don't all use the same "front" direction. If desks or chairs come out backwards, change the
FRONT_IS_NEG_Ysetting at the top of the script, undo, and re-run. - Door hinges: the doors are set to swing into the office, but I couldn't control which side they hinge on. Check a couple of doors and flip the hand if needed.
- Re-running: it skips walls that already exist and any office that already has a room, so it won't duplicate anything. Ctrl+Z undoes the whole run.
Dynamo returned this error: No desk chair family loaded or found in the Revit library. Load one via Insert > Load Family and re-run. File "<string>", line 265, in get_furniture File "<string>", line 348, in <module>
The script stops when it can't find a chair family. I'll change it so it searches the whole Revit library (plus any folders you list) and skips a missing furniture piece with a note, instead of stopping everything.
Next I'll update the bookshelf block so it uses that count and skips cleanly when no shelving family is available.
I've updated create_perimeter_offices.py, but I haven't run it in Revit. It passes a syntax check, and the layout simulation still shows the same office plan.
What went wrong: the script found a desk but no chair family. It only looked in the Furniture folder of the Revit library, and only for file names containing "chair". Your installed library may not include chairs at all; recent Revit versions download content libraries separately.
What changed:
- Wider search: it now looks for "chair", "seat" or "stool" across the whole Revit library, not just the Furniture folder. It also checks more categories among the families already loaded in your project.
- No more hard stop: if a piece of furniture still can't be found, the script builds everything else (walls, doors, rooms and the other furniture) and lists what it skipped in its output.
- One chair family is enough: if only one chair family turns up, it's used for both the desk chair and the guest chairs.
- Your own family folder: you can add a folder to
FAMILY_SEARCH_FOLDERSat the top of the script, and it will search there first.
Two easy ways to get chairs:
- Load any chair family through Insert › Load Family before you run the script.
- Run the script now. Once you've loaded a chair, delete the rooms on Levels 2–3 and run it again; it skips walls that already exist and only fills in the offices without rooms.
The earlier failed run may have loaded the desk and door families into your project. That's harmless, and the script will use them.
Add Stairs
Add a stair that connects Levels 1 though 4 near the southwest corner of the building
I'll put the stair in the southwest corner, in the space the SW corner office uses on Levels 2–3. It's the only spot near that corner that already has a corridor door on the upper floors.
I wrote create_sw_stair.py, which builds a U-shaped stair in the southwest corner running from Level 1 to Level 4. The Revit connection still can't change the model, so you'll need to run it in Dynamo. It has no syntax errors, and I checked the stair math for a 10' story, but I haven't run it in Revit.
The stair
- Location: it sits 2' in from the curtain wall in the SW corner, which keeps it clear of the corner column. The flights are 44" wide with a 4" gap between them, and the footprint is about 11' × 7'-8".
- Flights: each story gets its own stair, so the risers fit that story's actual height. The first flight climbs west to a landing at the west end. The second flight returns east and arrives near the corridor door.
- Risers and treads: risers are no taller than the stair type allows (7" by default), with 11" treads. A 10' story works out to 18 risers of 6.67", with 9 in each flight.
- Railings: added to every flight.
- Openings: one shaft cuts through Levels 2 and 3 and the roof over the flights and the middle landing. The arrival end at each level is left solid.
Changes to the SW corner office on Levels 2–3
- Room: the room is renamed "Stair 1", and its corridor door stays as the stair door.
- Furniture: the office furniture inside that room is deleted. Ctrl+Z restores it.
Running it
- Re-running: if you run it again, it skips any story that already has a stair there and doesn't add a second shaft.
- Undo: Ctrl+Z works, but it takes several presses: one per story, plus one for the shaft and room changes.
- Taller first story: the stair fits in the corner up to about a 16' story height. If Level 1 to Level 2 is taller than that, the script finishes but warns you to check the stair in plan.
Still to sort out
- Fire-rated enclosure: a four-story building will need this stair enclosed with fire-rated walls. Right now it's bounded only by the curtain wall and the office partitions.
- Roof opening: the opening at Level 4 is open to the sky. It needs a roof hatch or a small stair penthouse.
I can add the rated walls or the penthouse next if you'd like.
Dynamo returned this error: cannot import name 'StairsEditScope' from 'Autodesk.Revit.DB.Architecture' (unknown location) File "<string>", line 52, in <module>
That one is my mistake: StairsEditScope lives in Autodesk.Revit.DB, not in the Architecture namespace. I'll fix the import and make the stair imports check both namespaces, so a similar mix-up can't stop the script again.
I've fixed create_sw_stair.py. I'd imported StairsEditScope from Autodesk.Revit.DB.Architecture, but it actually lives in Autodesk.Revit.DB. The script now looks up each stair and railing class in both places, so another misplaced import won't stop it.
The failure happened while loading the script, before it touched the model, so you can just paste in the new version and run it again. It passes a syntax check, but I haven't run it in Revit.
Dynamo returned this error: PythonEvaluator.Evaluate operation failed. EditScope cannot be closed, for there is a transaction or transaction group still open in the document.
That message hides the real error. Something inside the stair step failed while its inner transaction was still open. My cleanup code then tried to cancel the stair edit before rolling that transaction back, and Revit refused, so you saw that refusal instead of the original problem. I'll fix the cleanup order and make the script report which step actually failed.
I've updated create_sw_stair.py. It still passes a syntax check, but I haven't run it in Revit, and I don't know yet whether this gets the stair built. What I fixed is the error reporting; the step that originally failed is still unknown.
What went wrong: one of the stair steps failed partway through. My cleanup code then cancelled the stair edit while that step's transaction was still open. Revit refused the cancel, and that refusal is the "EditScope cannot be closed" message you saw. The original error was lost.
What changed:
- Cleanup order: the cleanup now rolls back the open transaction first, then cancels the stair edit, so the original error comes through.
- Clearer errors: the message now names the step that failed, such as "create run 1" or "commit stair", along with Revit's own error text.
- Railings: these are now added after each stair is finished, in their own transaction. If they fail, the stair stays and you get a note instead.
- Dynamo check: before starting, the script checks whether Dynamo still has a transaction open and says so plainly if it does.
Running it again: the stair edit was cancelled, so nothing was left in the model. Paste in the new version and run it. If it fails again, send me the new message. It will show exactly which part to fix.
Dynamo returned this error: Stair Level 2 -> Level 3 failed at step 'create run 1': The input locationPath is not a valid location path line for straight run. Parameter name: locationPath at Autodesk.Revit.DB.Architecture.StairsRun.CreateStraightRun(Document document, ElementId stairsId, Line locationPath, StairsRunJustification justification) at InvokeStub_StairsRun.CreateStraightRun(Object, Span`1) at System.Reflection.MethodBaseInvoker.InvokeWithFewArgs(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) File "<string>", line 222, in <module>
Level 1→2 built fine, so the approach works. The run on the next story failed because I placed its path line at height 0. Revit wants it at that story's actual elevation, which only happens to be 0 on Level 1. I'll place each run at its story's real height and have the script detect how Revit measures the run heights instead of assuming.
I've fixed create_sw_stair.py. It passes a syntax check, but I haven't run it in Revit.
What went wrong: Revit expects each stair run to start at that story's actual elevation. My script always placed the first run at height 0. That happens to be Level 1's elevation, which is why the Level 1 → 2 stair was built and Level 2 → 3 failed.
What changed:
- Run height: the first run of each story now starts at that story's real elevation.
- Second run: the script checks how Revit reports the first run's top height, then starts the second run from there.
Running it again: paste in the new version and run it. It will see the Level 1 → 2 stair you already have and skip it. It will then build Level 2 → 3 and Level 3 → 4, cut the shaft, and turn the SW corner office on Levels 2–3 into the stair room. Since the last run stopped partway through, check that the Level 1 → 2 stair got its railings. If not, add them with Railing › Place on Stair.
Getting Advice on Placing Additional Stairs
Recommend options for placing a second stair in the building and explain the pros and cons of each option.
My recommendation is the northeast corner, diagonally opposite Stair 1. It's the only spot that clears the exit-separation rule with room to spare, whether or not the building has sprinklers.
The rule that decides this: the building code (IBC 1007.1.1) requires the two exits to be at least half the building's diagonal apart. That drops to one-third if the building is fully sprinklered. Your 82' × 82' footprint has a diagonal of about 116', so:
- No sprinklers: the stairs must be at least 58' apart.
- Fully sprinklered: at least 39' apart.
I measured each option from Stair 1's door, at roughly (17.6', 15.3') off the SW corridor corner, to the new stair's door. The distances are approximate.
Option | Separation | Without sprinklers | With sprinklers |
A. NE corner office | ~67' | Passes | Passes |
B. SE corner office | ~47' | Fails | Passes |
C. NW corner office | ~47' | Fails | Passes |
D. Middle of the north side | ~54' | Fails (just) | Passes |
E. Open stair in the atrium | n/a | Can't count as an exit | Can't count as an exit |
A. NE corner office (recommended)
- Pros:
- It has the most separation, which leaves room if the enclosures or doors move later.
- The plan stays symmetric.
- It's a mirror of Stair 1, so the existing script needs only small changes.
- People in any office have two clearly different directions to go.
- Cons:
- You lose a second corner office, which is usually the most valuable space.
- The NE corner office also has the north overhang above it at the roof, so roof access would need a hatch or penthouse there or at Stair 1.
B. SE corner office
- Pros:
- It sits under the south cantilever, so a roof penthouse there would read as part of that overhang.
- Cons:
- Both exits would be on the south side, so the north offices have farther to walk.
- It only works if the building is sprinklered.
C. NW corner office
- Pros: none to speak of.
- Cons:
- It has the same separation problem as B.
- Both exits would be on the west half of the building.
D. Middle of the north side (replacing an office)
- Pros:
- It's the shortest walk for the north and east offices.
- Corner offices stay as they are.
- Cons:
- It breaks the run of offices on that side.
- It's just short of the unsprinklered rule.
- It lands mid-span on the grid, between grids 2 and 3, so the stair opening needs extra framing.
E. Open stair in the atrium
- Pros:
- It's good for wayfinding and connects the floors visually.
- Cons:
- An open stair through four stories can't count as a required exit. You would still need one of options A–D.
- It takes up atrium floor area.
- It works well as a third stair, not as the second.
Two other things to settle while you decide:
- Accessibility: a four-story building needs an elevator. If it isn't sprinklered, the exit stairs also need 48" clear between handrails or an area of refuge; the current flights are 44". Putting the elevator, restrooms and the second stair together in the NE corner would solve several of these at once.
- Code check: treat this as early planning, not a code review. Confirm occupancy, sprinklers and how separation is measured with your code consultant or the local building department.
I can adapt create_sw_stair.py to build option A in the NE corner, or another option if you prefer.
Please build Option E
I'll build an open stair inside the atrium. Before I write it, here's the plan:
- Which floors it connects: Levels 1–2–3. Level 4 is the roof, and its atrium opening is open to the sky, so there's nothing for a stair to land on up there.
- Form: two 5'-wide switchbacks that hang into the atrium off the slab edges. Level 1→2 starts on the atrium floor and lands on the Level 2 slab edge along the south side. Level 2→3 starts on the Level 2 slab edge along the north side and lands on the Level 3 slab edge.
- Why opposite sides: the two flights don't stack, so each has full headroom. Getting from one to the other means walking half the corridor loop on Level 2.
I wrote create_atrium_stair.py, which builds the open stair in the atrium. It has no syntax errors, and I checked the layout math for 10' floor-to-floor heights. I haven't run it in Revit; it uses the same stair-building steps that just worked for the SW stair.
The stair
- Level 1 → 2: starts on the atrium floor along the south edge. It climbs north, turns at a landing, and arrives on the Level 2 slab edge at the corridor.
- Level 2 → 3: the same switchback, turned around to sit on the north edge.
- Size: two 5'-wide flights, 9 risers of 6.67" each at a 10' story. Each stair reaches 12'-4" into the atrium and is about 10'-4" wide, centered on its edge.
- Clear space: about 15' of open atrium remains between the two landings.
- Railings: added to every flight.
Things to know
- Not an exit: an open stair through the atrium doesn't count toward required exits, so the building still needs a second enclosed exit stair. The NE corner (option A) is still the best place for one.
- Atrium guardrails: the slab edges around the atrium on Levels 2–3 have no guardrails yet. That open edge needs a guard everywhere except where the stairs connect.
- Supporting steel: each landing hangs about 12' out from the slab edge, so the engineer will need to design the hanger or support steel.
- Running it: paste it into a Python Script node (CPython3 or PythonNet3) and run. Running it twice skips any stair that's already there. Ctrl+Z undoes it, one step per stair plus one for each set of railings.
I can add the atrium guardrails next, or adapt the SW stair script for the NE exit stair.
The Results from the First Time That We Followed These Steps Were Different (the prompts were slightly different)
In that prior run, Claude recommended:
Note: In implementing option 2, Claude understood that the staircase provided access to the roof and took the appropriate action — adding a landing at that level without prompting. It understood the design context and the code requirements, and acted on them.