<jupyter_start><jupyter_text>Software Development Environments  tl;dr * Integrated Development Environments (IDEs): we recommend you use one for software development ✔ * Jupyter notebooks: great for tutorials and as a playground for getting familiar with code, but not great for software engineering 🚸 * plain text editors: try to avoid, although sometimes you have to use one ⛔  Integrated Development Environments (IDEs) * All-in-one that comes with many features to help to code * We think it is worth the (small) effort to learn to use one * The two leading IDEs for Python are VS Code and PyCharm * We will be demoing useful features in VS Code throughout the week * Demo:    * VS Code workspace introduction    * Autocomplete    * Static error checking (linting)    * Git integration    * SSH integration  Jupyter Notebooks  * Combination of code cells and Markdown text cells make it useful for writing tutorials * Running/rerunning one cell at a time allows you to play around with the code/understand how it works * Useful for plotting, solving a problem (think of it as a document) * Output depends on order cells are run/rerun -> not good for repeatability * Not designed for programming a software package  Plain text editors* The "old-school" editors (e.g., vim, emacs, nano, notepad++)* We generally recommend you avoid doing large amounts of programing in them as the code is prone to bugs* Sometimes inevitable in astronomy so it is good to learn a little bit of either vim or emacs* You can [use VS Code over ssh](https://code.visualstudio.com/docs/remote/ssh), so you should not need to use these very often!  Object-Oriented and Functional Programming  tl;dr * Object-oriented programming relies on the state of variables to determine the output    * Good to keep track of something that is changing (e.g., the number of people in a Zoom meeting) * Functional programming relies solely on the inputs, which do not change, to determine the output    * Good for math equations (e.g., computing the inverse of a matrix) * Typically, you will want a mix of both programming paradigms  Object-Oriented Programming ![](imgs/oo-meme.png)  Classes * Classes organize variables and functions into a single object * Objects can be used to track state - useful model for many things in the world * Inheritance: takes fields/functions from a super class (or parent class) - useful for reorganizing/reusing code * Refer to the diagnostic notebook for the basics on class and superclass syntax Activity Goal * Finish the following free fall gravity simulator. Use your simulation to determine how long it takes for a particle to fall to the ground from a height of 10 meters. We will poll everyone on what result they get. * Bonus activity: In the future, we want particles that experience other forces and move in 3D. Write a `Particle` superclass that the `FreeFallParticle` is a subclass of. What fields go into the `Particle` class?  End-product * Reply to the Piazza post (anonymously is totally fine) with your result on how long it takes for a particle to fall to the ground from a height of 10 meters.  Roles * Driver: in charge of sharing their screen and typing the code for this activity (the person that lives at the highest latitude) * Recorder: in charge of writing down all the results and reporting back when we poll everyone (the person with the hottest temperature outside currently) * Time-keeper: in charge of keeping the team on track to finish the activity (the person who experienced rain the most recent) * Navigator: in charge of directing the driver what to code (everyone else; can be more than one person) Instructions * Figure out who is in white role based on criteria above * Read through the current code and discuss as a team what needs to be done to complete the main goal (how long it takes for a particle to fall from 10 m) * Code it up and get an answer! * If you have time, discuss as a team how to accomplish the bonus activity and code that up as well. Check that you get the same answer from how long it takes a particle to fall from a height of 10 meters.Note that there is no single right answer for either activity!<jupyter_code># Object-Oriented Programming

class Particle(object):
    """
    A simulated particle that moves in 3D

    TOOD: this is just a place holder for now. We need to figure out what fields go in here. 
    """
    def __init__(self):
        pass


class FreeFallParticle(Particle):
    """
    Simulate a particle falling due to Earth's gravity. Particle is stationary at first

    Args:
        height (float): a height in meters
        dt (float): timestep of the simulation in seconds
    """
    def __init__(self, height, dt=0.1):
        """
        Function that is run to initialize the class
        """
        # let's initalize it's parent class (empty for now because it is a blank class)
        super().__init__()

        # note that we are not using the astropy.units class here as we haven't talked about it yet! But it could be useful!
        self.height = height # current height [meters]
        self.velocity = 0 # current velocity [meters/second]
        self.time = 0 # time elapsed [seconds]
        self.dt = dt # timestep of the simulation [seconds]
        self.g = -9.8 # gravitational acceleration (Don't change) [meters/second^2]


    def get_num_steps_run(self):
        """
        Function that returns the number of timesteps that have run by comparing self.time with self.dt

        Returns:
            num_steps (int): number of time steps already completed in the simulation
        """
        num_steps = int(self.time / self.dt)
        return num_steps

    ##### Activity ######
    """
    Add functionality to advance the particle's height by one time step at a time. (hint: implement the function below).
    Then use this code to calculate how long it takes for the particle to fall down from a height of 10 meters.

    Some useful equations for how to calculate the particle's new state at the next time step.
    Pseudo code below:
    acceleration = g
    new_velocity = current_velocity + acceleration * dt
    new_height = current_height + new_velocity * dt

    Add inputs and outputs. 
    """
    def simulate_timestep(self):
        """
        Advance the simulation time by a single timestep (self.dt). 
        Update the simulation with the new time, height, and velocity

        Returns:
            height (float): the current height in meters
        """
        return 0. # currently does nothing
# Here's how you could call this function
ball = FreeFallParticle(10) # start out a 10 m above the ground
print(ball.time, ball.height)
ball.simulate_timestep()
print(ball.time, ball.height) # time should move forward by 0.1 seconds<jupyter_output><empty_output><jupyter_text>Object Oriented Programming     * Code structured around objects * Depends on changing/"mutable" state of the object (e.g., `self.height`, `self.velocity`, etc.) * Most things in the world change, so it makes sense to frame things in this way * We recommend identifying entities that should become objects and program around this   * For example, particles in a simulation can be grouped together in a classSome more subtle things to consider when using classes  * Creating an object can be slow. Too many object creations can slow down code  * Could be prone to bugs since function outputs depends on both inputs and the current state of the objectHere's some resources to learn more about object-oriented programming for Python  * More conceptual discussion: https://www.educative.io/blog/how-to-use-oop-in-python  * More features that can be used: https://www.programiz.com/python-programming/object-oriented-programming    Functional Programming![](imgs/fp-meme.png)  Functional Programming * Key paradigm: functions outputs depend solely on the inputs    * Easier to guarantee correctness    * More messy to track changing state of things * Functional programming != no objects. Objects however are static data structures.    * You need to create a new object if you want to change an object * Useful for math problems, physics equations, unit conversions    * `import astropy.units as u; u.m.to(u.nm)`    * Data pipeilnes where given input should always return given output  Object Oriented vs Functional Programming * Object oriented programming is good when things change (e.g., the position of a planet, the current image being analyzed) * Functional programming is good to deterministic things (e.g., math equations, making sure you do not accidentally apply the same function twice) * Most packages use both (whether they think about it or not) * Object-oriented and functional programming are tools to solve a engineering problem. Each company or software group will have different approaches to tackling a problem given these tools. If it works for your team to solve a problem and keep the code maintainable, then it is a good solution. There is no one right answer!<jupyter_code># Functional Programming Example. 

class FuncParticle(object):
    """
    A particle with a given height and vertical instantaneous velocity

    Args:
        height (float): height of the object currently in meters
        velocity (float): velocity of the object in meters. Default is 0 (at rest)
    """
    def __init__(self, height, velocity=0):
        self.height = height
        self.velocity = velocity

def freefall_timestep(thing, dt=0.1):
    """
    Simulate free fall of the particle for a small time step

    Args:
        thing (FuncParticle): the current position and velocity of the particle
        dt (float): optional float that specifies the timestep in seconds

    Returns:
        new_thing (FuncParticle): the updated position and velocity of the particle
    """
    dt_units = dt
    new_velocity = thing.velocity + -9.8 * dt_units
    new_height = thing.height + new_velocity * dt_units

    new_thing = FuncParticle(new_height, new_velocity)
    return new_thing


ball = FuncParticle(1) # start a ball at 1 m
ball_states = [ball]
print(0, ball.height)
dt = 0.1
time = 0

for i in range(5):
    new_ball = freefall_timestep(ball_states[-1], dt)
    ball_states = ball_states + [new_ball,]
    time += dt
    print(time, new_ball.height)


# Running the function with the same inputs will return the same result
# This generally would not happen with object oriented programming
# When is this good or bad?
output_ball_1 = freefall_timestep(ball, dt)
output_ball_2 = freefall_timestep(ball, dt)
print("Are these the same?", output_ball_1.height, output_ball_2.height)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.057862107833928235, 'fraction_numerical': 0.004133007702423445, 'mean_word_length': 3.6923754958131334, 'pattern_counts': {'":': 0, '<': 9, '<?xml version=': 0, '>': 10, 'https://': 3, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 3, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '2', 'dir': 'jupyter-structured-clean-dedup', 'id': '57378', 'license': 'BSD-3-Clause', 'n_tokens_mistral': 2759, 'n_tokens_neox': 2604, 'n_words': 1457, 'path': 'Day1/code_developing.ipynb', 'repo_name': 'PauliPalbi/codeastro', 'size': '10646'}